SevenMock is a light-weight Java dynamic mock objects framework. It is unusual in that it places responsibility for verifying operation parameters directly on the unit test code. This enables the test designer to write very clear, precisely targeted tests and makes test failures easier to diagnose.

Download SevenMock-2.1.0.zip (2008-06-17) (Requires Java 1.5 or later)
Download SevenMock-1.1.zip (2008-03-11)
SevenMock on SourceForge

Mock object frameworks create instances of mock objects that can be used in place of the real thing for unit testing. This enables a component to be tested in isolation even if it has dependencies on other components. The mock object is invoked by the component/object under test and supplies a pre-determined response. In addition, most mock objects are able to test the detail of method calls made to it e.g. for sequence and parameter values. Where this is not necessary, the mocks are sometimes referred to as 'stubs'.

To read about other Mock Objects implementations, see http://en.wikipedia.org/wiki/Mock_object

SevenMock works in a similar way to other Java dynamic mock object frameworks - by generating objects (using Java dynamic proxies) at the request of a unit test, that can be directly substituted for the real thing. The difference between SevenMock and other mock object frameworks is in the way that expectations are specified and executed - with SevenMock, the expected operation is specified by using an anonymous inner class that acts as a listener. When an operation is invoked on the mock object, SevenMock calls back to the test. The framework verifies that the correct operations are invoked in the correct order, but it is then the responsibility of the test to make whatever checks are necessary on the operation parameters.

This has the following advantages:

  • It is simple for the test to make as many or as few assertions on the operation parameters as necessary. This is useful because it is important that tests only verify things relevant to the scope of the test. Unnecessary or irrelevant assertions tend to make tests fragile because they can be broken by code changes that shouldn't strictly affect them. They also tend to obscure the meaning of the test, making it harder to determine the requirement that it is meant to be testing.
  • Since assertions are coded explicitly in the test class and executed in-place (rather than being evaluated by the mock object runtime), any assertion failure will generate a stacktrace that points directly to the assertion and test that failed, as well as the operation call that caused the failure. This makes test debugging much easier, as the developer does not then have to rely on their interpretation of the message provided by the mock object framework.
  • The mock implementation itself is very simple because it doesn't actually need to do much - the whole framework is just a few classes. This hopefully means that there is less to go wrong and should mean that it is easy to maintain. The API is also very simple with just a few operations.

On the negative side, the anonymous class mechanism means that it is not possible to directly mock interfaces or abstract classes. The work-around for this is to use an IDE to create an adapter class (i.e. a class with empty implementations of all abstract operations) for the interface.

The diagram below shows the relationship between the test, SevenMock and the code being tested. The test sets up expectations by calling the MockControl.expect() method, passing an anonymous inner 'listener' class. This inner class extends the class being mocked and overrides a single method - the one that is expected to be called. When the test is run, the Mock ensures that this method is invoked in place of the original, which enables the developer to make any necessary assertions on the parameters passed in.

The code example below shows a test asserting that the method FundsTransferManager.transferMoney() in turn calls AccountManager.deductFunds() and AccountManager.depositFunds(). Normally the setup of the mock would be performed in the test's constructor or setUp method for convenience - see SevenMockTest.java in the distribution archive for an example.

public void testMoneyTransfer() {
        MockController mockControl = new MockController();
        AccountManager acntMgrMock = mockControl.getMock(AccountManagerImpl.class);    // Create an AccountManager mock
        FundsTransferManager ftMgr = new FundsTransferManager(acntMgrMock);      // Use dependency injection to make sure the mock is used

        mockControl.expect(new AccountManagerImpl() {             // Expect the AccountManager.deductFunds() method to be called
            public void deductFunds(int acntNum, int amount) {
                assertEquals(ACNT_NUM_1, acntNum);             // Perform assertions on the parameters
                assertEquals(AMOUNT, amount);
            }
        });
        mockControl.expect(new AccountManagerImpl() {             // Expect the AccountManager.depositFunds() method to be called
            public void depositFunds(int acntNum, int amount) {
                assertEquals(ACNT_NUM_2, acntNum);             // Perform assertions on the parameters
                assertEquals(AMOUNT, amount);
            }
        });
        ftMgr.transferMoney(AMOUNT, ACNT_NUM_1, ACNT_NUM_2);   // Invoke transferMoney(), passing constants as parameters
        mockControl.verify();                                  // Tell the MockControl that the test has finished
    }

Unequenced Expectations

Expectations created with the expect() operation are assumed to occur in strict sequence. SevenMock will verify the sequence that calls are received in and will throw an UnexpectedOperationError if expectations are encountered out of sequence.

Additionally, SevenMock allows developers to specify expectations that have no implied sequence using the expectAtAnyTime() operation, or to mix sequenced and unsequenced expectations. In the latter case, SevenMock will attempt to match sequenced expectations first, checking the unsequenced expectations only if no match is found.

The full list of expectation operations supported by SevenMock is as follows:

  • expect(Object listener) - Expect a call once, in the sequence the expectations are defined.
  • expect(int expectedOccurrences, Object listener) - Expect the specified number of calls, in the sequence the expectations are defined.
  • expectOnceAtAnyTime(Object listener) - Expect exactly one call, but at any time during the test.
  • expectAtAnyTime(Object listener) - Expect any number of calls at any time during the test.
  • expectAtAnyTime(int expectedOccurrences, Object listener) - Expect the specified number of calls, but at any time during the test.
  • How it Works


    SevenMock 2.0 works with Java 1.5 and later. Users of older Java versions should use SevenMock 1.1.

    See also:

    Acknowledgements

    SevenMock was originally developed by Colin Cassidy on 7irene time as part of the test platform for their Archetype development product suite. 7irene was acquired by Prolifics in 2007. SevenMock has been used by 7irene and Prolifics on a number of client consultancy engagements and development projects.

    I would like to thank my colleagues at 7irene for their help and feedback and for allowing me to make SevenMock open source software. I would also like to thank SourceForge user adrianshum for his help and suggestions.

    Mock Objects Resources


    SourceForge.net Logo