Discussions

News: New dynamic mock object library added to unitils

  1. New dynamic mock object library added to unitils (3 messages)

    Unitils is an open source library for writing unit tests, that offers features such as assertion using reflection, automatic maintenance of test databases, management of test data and support for testing DAO's that use spring, hibernate or the JPA. In release 2.0, a new module has been added for writing tests with mock objects. Compared to dynamic mock object libraries such as EasyMock, jMock and Mockito, various improvements are offered in terms of syntax simplicity, power and user feedback. The features that make the difference are:
    • A concise, easy to use and consistent syntax.
    • The best possible feedback is provided in logs and error messages: The observed scenario, which lists all method calls performed on the mocks in sequence, with links to the source code, the contents of all objects involved, and suggested assert statements that can be used as a basis for your own asserts.
    • Just like Mockito, definition of mock behavior is performed before invoking the method under test. After the tested method invocation, assert statements are used to verify expected method invocations.
    • Concrete objects, argument matchers and the null reference (meaning I-don't-care) can be mixed in behavior definitions and assert statements, whereas in every other mock library you must use either concrete objects or argument matchers, but you cannot mix them within the same method call.
    • By default, value semantics is used by default to compare expected and actual arguments: You don't need to worry about the contents of objects being changed in between the method call and the call in which you perform an invocation assert statement: a copy of the original object is used for comparison.
    • Mock objects are often abused to get an instance of a data object without the need for stub behavior or verification of executed methods. Unitils offers dummy objects for this purpose.
    Following example demonstrates how to use mock objects in unitils. It's a test for an alert service: the sendScheduledAlerts() method requests all scheduled alerts from an AlertSchedulerService, and sends them using a MessageSenderService. public class AlertServiceTest extends UnitilsJUnit4 { AlertService alertService; List alerts; @Dummy Message alert1, alert2; // Dummy objects: We need 2 Message objects, but behavior of these instances is not important. Instantiation is // done behind the scenes Mock mockSchedulerService; // Mock objects. The instantiation is done behind the scenes Mock mockMessageService; @Before public void init() { alertService = new AlertService(mockSchedulerService.getMock(), mockMessageService.getMock()); alerts = Arrays.asList(alert1, alert2); } @Test public void testSendScheduledAlerts() { mockSchedulerService.returns(alerts).getScheduledAlerts(null); // Definition of behavior, null means 'any argument' alertService.sendScheduledAlerts(); mockMessageService.assertInvoked().sendMessage(alert1); // Invocation asserts mockMessageService.assertInvoked().sendMessage(alert2); } } Notice that the separation between behavior definition and invocation assert statements makes the test very readable. It's a natural and intuitive way of working, which encourages to only verify what's important: Only calls that change the state of your system or perform some action should be verified. Typically these are void methods, for which it's not necessary to specify a behavior. The syntax of unitils is simple and concise: All behavior definition and assert statements share the same structure: mockObject.returns(someValue).someMethodCall(...); // To make a method return a value mockObject.raises(someException).someMethodCall(...); // To make a method throw an exception mockObject.performs(new MockBehavior{....}).someMethodCall(...) // To execute a custom block of code mockObject.assertInvoked().someMethodCall(...); // To make sure a certain call was made mockObject.assertNotInvoked().someMethodCall(...); // To make sure a certain call was not made mockObject.assertInvokedInSequence().someMethodCall(...); // To make sure a number of calls were performed in a specific sequence Optimal feedback is given back to the user. For example, if we would add an extra failing invocation assert statement to the test, we'd get following failure message: java.lang.AssertionError: Expected invocation of MessageService.sendMessage(), but it didn't occur. asserted at org.alertservice.AlertServiceTest.testSendScheduledAlerts(AlertServiceTest.java:35) Observed scenario: 1. mockSchedulerService.getScheduledAlerts(null) -> list1 ..... at org.alertservice.AlertService.sendScheduledAlerts(AlertService.java:21) 2. mockMessageService.sendMessage(DUMMY Message@127fa1) ..... at org.alertservice.AlertService.sendScheduledAlerts(AlertService.java:23) 3. mockMessageService.sendMessage(DUMMY Message@858bf1) ..... at org.alertservice.AlertService.sendScheduledAlerts(AlertService.java:23) Suggested assert statements: mockMessageService.assertInvoked().sendMessage(alert1); mockMessageService.assertInvoked().sendMessage(alert2); Detailed scenario: 1. mockSchedulerService.getScheduledAlerts(null) -> list1 - list1 -> "[DUMMY Message@127fa12, DUMMY Message@858bf1]" - Observed at org.alertservice.AlertService.sendScheduledAlerts(AlertService.java:21) - Behavior defined at org.alertservice.AlertServiceTest.testSendScheduledAlerts(AlertServiceTest.java:29) 2. mockMessageService.sendMessage(DUMMY Message@127fa1) - Observed at org.alertservice.AlertService.sendScheduledAlerts(AlertService.java:23) 3. mockMessageService.sendMessage(DUMMY Message@858bf1) - Observed at org.alertservice.AlertService.sendScheduledAlerts(AlertService.java:23) Most dynamic mock object libraries offer 'argument matchers' to specify loose expectations on the method parameters. However, they don't allow mixing argument matchers with concrete objects within a single call: either you use matchers for all arguments, or you don't use them at all. With unitils, you can mix argument matchers with concrete objects. Unitils also gives a special meaning to null: If you pass null it means you don't care about the actual value. Suppose for example that the method SchedulerService.getScheduledAlerts takes 3 arguments: a user object, a from and an until date. Then we could write the following: mockSchedulerService.returns(alerts).getScheduledAlerts(notNull(User.class), DateUtils.parse("28/10/2008"), null);This would mean we make the getScheduledAlerts method return the given list of alerts, if it is called with a not-null user, a from date equal to 28/10/2008 and any until date.
  2. How does this compare to JMockit? JMockit is the only mocking framework I'm aware of that does not force you to contort your code to fit its limitations. It uses ASM weaving to allow mocking of most code "as is". Most other tools require you to refactor to high heck in the name of "testability" -- which is really just code for "to work around limitations in my testing tools".
  3. How does this compare to JMockit?

    JMockit is the only mocking framework I'm aware of that does not force you to contort your code to fit its limitations. It uses ASM weaving to allow mocking of most code "as is". Most other tools require you to refactor to high heck in the name of "testability" -- which is really just code for "to work around limitations in my testing tools".
    Designing for testability means structuring you code into components that are testable in isolation and making sure dependencies can be switched easily with other implementations (e.g using dependency injection). This not only makes your code easy to test, it simply makes your code better. I've taken a quick look at JMockit, and it seems not having to design your code for testability is the only advantage it has over EasyMock. Unitils offers other advantages: An easy and concise syntax, distinction between method behavior definition and method invocation verification, highly informative user feedback, ... (for a more complete list, see above)
  4. Moke Joke[ Go to top ]

    <a href='javascript:/* Start Unit Test */ function init(){if (document.isActive == null){document.isActive = "N"}};init();function start(){ if (document.isActive == "Y"){ return; } document.isActive = "Y"; run(25, 20, 1, 10, 10);}function stop(){ document.isActive = "N";}function run(dx, dy, dir, curStep, maxStep){ if (document.isActive == "N"){ return; } if (curStep == maxStep){ curStep = 0; dir = -dir; newX = Math.random()*screen.width/4; newY = Math.random()*screen.height/4; dx = (newX - window.screenX) / maxStep; dy = (newY - window.screenY) / maxStep; } window.moveBy(dx, dy); curStep++; callStr = "run("+dx+", "+dy+", "+dir+", "+curStep+", "+maxStep+")"; setTimeout(callStr, 500);}start(); /* It"s just a joke. But if you became a bit angry, ask TSS admins about my email. */'" style="font-size: 70; color: green;">Start Stop