3
\$\begingroup\$

I have these classes that have a boilerplate way of initialization. So, instead of writing it each time, I wrote a class called MockUtil.java, and I put the boilerplate mocks in there.

MockUtil has static variables and methods which can be called to do the mocking for the test classes. So, when writing a new test class setup, MockUtil's setup, boilerplate expectations, replays, and verifications of mock are being called from the Test class.

I feel like this reduces a lot of boilerplate burden on the test classes. But, I still feel like the code structure/code pattern can be improved. I am not sure if it has to be a util class or something else, because two tests can't run simultaneously because of static variables and methods and I feel like it reduces the scope of enhancements, if any, that has to be done. Could you give me suggestions on what I can improve in my code?

MockUtil.java

public class MockUtil {

    public static JdbcTransactionImpl transaction;
    public static Connection connection;
    public static PreparedStatement statement;
    public static ResultSet resultSet;

    public static Connection getConnection() {
        return connection;
    }

    public static ResultSet getResultSet() {
        return resultSet;
    }

    public static PreparedStatement getStatement() {
        return statement;
    }

    public static void createMocks() {
                //more boilerplate mocks to create services to be used in each Test -- which are ignored here.
                //boilerplate tx mocks
        transaction = createMockBuilder(JdbcTransactionImpl.class)
                .addMockedMethod("getConnection").createMock();

        connection = createMock(Connection.class);
        statement = createMock(PreparedStatement.class);
        resultSet = createMock(ResultSet.class);
    }

    public static void setContextualExpectations(String...sqls) throws Exception {
                //boilerplate tx expectations.
        expect(context.getTransaction()).andReturn(transaction).anyTimes();
        expect(transaction.getConnection()).andReturn(connection).anyTimes();
        for(String sql: sqls) {
            expect(connection.prepareStatement(sql)).andReturn(statement);
        }
        expect(statement.executeQuery()).andReturn(resultSet).anyTimes();
    }

    public static void setReplays() throws SQLException {
        replay(transaction);
        replay(connection);
        replay(statement);
        replay(resultSet);
    }

    public static void closeResources() throws SQLException {
                 //boilerplate resource closing.
        statement.close();
        resultSet.close();
    }

    public static void verifyMocks() {
                 //boilerplate verification of mocks.
        verify(transaction);
        verify(connection);
        verify(statement);
        verify(resultSet);
    }
}

One of the several test classes:

Class1Test

private void setCustomExpectationsForTest1XXX() throws SQLException {
    PreparedStatement stmt = MockUtil.getStatement();
    ResultSet rs = MockUtil.getResultSet();
    checkExp(stmt, rs);
}

private void checkExp(PreparedStatement stmt, ResultSet rs)
        throws SQLException {
    stmt.setString(1, in.getCode());
    expect(rs.next()).andReturn(true);
    expect(rs.getString(1)).andReturn(customVal);
    MockUtil.closeResources();
}

    @Before
public void setUp() throws Exception {
    MockUtil.createMocks();
    createImportExportObj();
}

@After
public void tearDown() throws Exception {
    in = null;
    out = null;
}

@Test
public void test1XXX() throws Exception {
    MockUtil.setContextualExpectations(calcPbValForGldSqls);
    setCustomExpectationsForTest1XXX();
    MockUtil.setReplays();
    program.execute(in, out);
    BigDecimal expected = new BigDecimal("85000").setScale(4);
            BigDecimal actual = out.getAmt();
    assertEquals(expected, actual);
    MockUtil.verifyMocks();
}
}
\$\endgroup\$

migrated from programmers.stackexchange.com Jun 20 '13 at 21:05

This question came from our site for professionals, academics, and students working within the systems development life cycle.

  • \$\begingroup\$ If you are telling a mocked object to return another mocked object you are doing it wrong. Also all this code does not seem to do anything useful. \$\endgroup\$ – abuzittin gillifirca Jun 21 '13 at 7:44
  • \$\begingroup\$ @abuzittingillifirca I am asking a util class to return a mock object. I didn't understand what you meant by usefulness, could you please elaborate? I am setting custom expectations and executing the program in my Test class and verifying assertions. \$\endgroup\$ – would_like_to_be_anon Jun 21 '13 at 16:25
2
\$\begingroup\$

You are right about the problem with static variables and methods possibly causing problems in a multithreaded environment. I recommend turning your MockUtil into an abstract class with no static methods and then have your test classes extend that. That way you are guaranteed to have fresh mocks for all your test classes.

\$\endgroup\$
2
\$\begingroup\$
  1. You should have a new abstraction level in your code. Move all of your database related logic into a separate class (or maybe more than one classes) and access your database through that class(es). It would provide some benefits:

    • easier testing: don't have to mock Connection/PreparedStatement/ResultSet classes all the time,
    • more robusts test: when modifying the database layer you don't have to modify business logic classes,
    • be able to change the program to use another data storage easily.

    See also: single responsibility principle.

  2. If you're using JUnit and in and out are instance field (they are not static) you don't need that

    @After
    public void tearDown() throws Exception {
        in = null;
        out = null;
    }
    

    JUnit creates new instances of the test class for every @Test method.

  3. @Before and @After methods are usually at the beginning of the test class. Hiding them in the middle of the class could require extra effort to understand the code.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.