Difference between revisions of "Simplifying Mock Object Testing"

From CitconWiki
Jump to navigationJump to search
(New page: Facilitated by Tom Adams == Overview == * Tom walked through setting up a unit test using mocks (using JMock 1). * He demonstrated how hard it can be to read even the most simple interact...)
 
Line 7: Line 7:
 
* A superclass for mock object testing was created to house the simplified scaffolding
 
* A superclass for mock object testing was created to house the simplified scaffolding
 
* The unit tests became so simple that they appeared to simply be duplicating production code
 
* The unit tests became so simple that they appeared to simply be duplicating production code
 +
* [http://adams.id.au/blog/2007/01/simplifying-mock-object-testing/ Tom's original blog entry on the topic]
  
 
== Tools people mentioned ==
 
== Tools people mentioned ==
Line 22: Line 23:
 
* Use annotations to further reduce scaffolding and make roles more explicit (auto mocking)
 
* Use annotations to further reduce scaffolding and make roles more explicit (auto mocking)
 
* At some point, the behavioural tests mirror production code and perhaps for non-state-based testing at some point the value is reduced
 
* At some point, the behavioural tests mirror production code and perhaps for non-state-based testing at some point the value is reduced
 +
 +
== Process ==
 +
 +
The example uses jMock 1.2, though the same process has been successfully applied to EasyMock 2.
 +
 +
=== 1. Initial state ===
 +
 +
Creating a class that requires the use of a stack.
 +
 +
TODO
 +
 +
=== 2. Lose the controller, only deal with the mock ===
 +
 +
TODO
 +
 +
=== 3. Pull mocks & subject out as fields ===
 +
 +
TODO
 +
 +
 +
Note. You may need to {{reset()} the mocks between tests if you use a framework such as TestNG that doesn't create a new instance of the test case for each test method (i.e. mocks will share state).
 +
 +
=== 4. Automocking ===
 +
 +
TODO
 +
 +
=== 5. Use a framework that does all this for you ===
 +
 +
If all this is too much hard work, you have a couple of options. Push the code out into a class & delegate or push into a super class and (in JUnit) auto-create from {{runBare()}}.
 +
 +
Even easier, use a framework that does all this for you such as [http://code.googlecode.com/p/instinct/ Instinct] or Boost.
 +
 +
{{
 +
package com.googlecode.instinct.example.stack;
 +
 +
import static com.googlecode.instinct.expect.Mocker12.expects;
 +
import static com.googlecode.instinct.expect.Mocker12.mock;
 +
import static com.googlecode.instinct.expect.Mocker12.same;
 +
import static com.googlecode.instinct.expect.Mocker12.verify;
 +
import com.googlecode.instinct.internal.util.Suggest;
 +
import com.googlecode.instinct.marker.annotate.AfterSpecification;
 +
import com.googlecode.instinct.marker.annotate.BeforeSpecification;
 +
import com.googlecode.instinct.marker.annotate.Dummy;
 +
import com.googlecode.instinct.marker.annotate.Context;
 +
import com.googlecode.instinct.marker.annotate.Mock;
 +
import com.googlecode.instinct.marker.annotate.Specification;
 +
import com.googlecode.instinct.marker.annotate.Subject;
 +
 +
@Context
 +
public final class MagazinePileContext {
 +
    @Dummy private Magazine magazine;
 +
    @Mock private Stack<Magazine> stack;
 +
    @Subject private MagazinePile magazinePile;
 +
 +
    @BeforeSpecification
 +
    public void setup() {
 +
        magazinePile = new MagazinePileImpl(stack);
 +
    }
 +
 +
    @Specification
 +
    void callsPushOnStackWhenAddAMagazineIsAddedToThePile() {
 +
        expects(stack).method("push").with(same(magazine));
 +
        magazinePile.addToPile(magazine);
 +
    }
 +
}
 +
}}
 +
 +
== What next? ==
 +
 +
* Annotations for defining the role of the test doubles; mocks, dummies, stubs, etc.
 +
* Better framework support for partials, etc.
 +
* Auto-creation of test subject - if constructor takes fields that are mocked out in the test can auto-create the test subject.
 +
* Auto-injection of Spring/Guice/Pico beans.
 +
* Auto-creation of arrays filled with mocks.
 +
* Auto triangulation - Framework takes care of creating (sensible) random values in order to drive out correct code.
 +
* ...?
 +
 +
== Downsides ==
 +
 +
* Lack of explicitness - as you don't have the code in every test, need to go hunting for how things are created.
 +
* Investment in understanding the mocking infrastructure; lifecycle, etc.
 +
* Need to roll your own or use a non-"standard" framework such as Instict.

Revision as of 16:20, 30 July 2007

Facilitated by Tom Adams

Overview

  • Tom walked through setting up a unit test using mocks (using JMock 1).
  • He demonstrated how hard it can be to read even the most simple interactions due to the amount of scaffolding required
  • The group contributed to DRYing up the scaffolding
  • A superclass for mock object testing was created to house the simplified scaffolding
  • The unit tests became so simple that they appeared to simply be duplicating production code
  • Tom's original blog entry on the topic

Tools people mentioned

  • JMock 1
  • JMock2 wraps “mock” and “controller” in one
    • Side effect: need to have some way of explicitly moving from expectation-setting mode and replay mode. JMock2 is closer to the EasyMock style of explicit value seeding and method replay.
  • EasyMock
  • RMock
  • PicoUnit

Summary / comments

  • DRY up your mock setup
  • Create methods that express intent, e.g. Stack stack = makeMock(Stack.class);
  • Put these methods into an abstract testing class (e.g. MockObjecTestCase) for reuse
  • Use annotations to further reduce scaffolding and make roles more explicit (auto mocking)
  • At some point, the behavioural tests mirror production code and perhaps for non-state-based testing at some point the value is reduced

Process

The example uses jMock 1.2, though the same process has been successfully applied to EasyMock 2.

1. Initial state

Creating a class that requires the use of a stack.

TODO

2. Lose the controller, only deal with the mock

TODO

3. Pull mocks & subject out as fields

TODO


Note. You may need to {{reset()} the mocks between tests if you use a framework such as TestNG that doesn't create a new instance of the test case for each test method (i.e. mocks will share state).

4. Automocking

TODO

5. Use a framework that does all this for you

If all this is too much hard work, you have a couple of options. Push the code out into a class & delegate or push into a super class and (in JUnit) auto-create from .

Even easier, use a framework that does all this for you such as Instinct or Boost.

{{ package com.googlecode.instinct.example.stack;

import static com.googlecode.instinct.expect.Mocker12.expects; import static com.googlecode.instinct.expect.Mocker12.mock; import static com.googlecode.instinct.expect.Mocker12.same; import static com.googlecode.instinct.expect.Mocker12.verify; import com.googlecode.instinct.internal.util.Suggest; import com.googlecode.instinct.marker.annotate.AfterSpecification; import com.googlecode.instinct.marker.annotate.BeforeSpecification; import com.googlecode.instinct.marker.annotate.Dummy; import com.googlecode.instinct.marker.annotate.Context; import com.googlecode.instinct.marker.annotate.Mock; import com.googlecode.instinct.marker.annotate.Specification; import com.googlecode.instinct.marker.annotate.Subject;

@Context public final class MagazinePileContext {

   @Dummy private Magazine magazine;
   @Mock private Stack<Magazine> stack;
   @Subject private MagazinePile magazinePile;
   @BeforeSpecification
   public void setup() {
       magazinePile = new MagazinePileImpl(stack);
   }
   @Specification
   void callsPushOnStackWhenAddAMagazineIsAddedToThePile() {
       expects(stack).method("push").with(same(magazine));
       magazinePile.addToPile(magazine);
   }

} }}

What next?

  • Annotations for defining the role of the test doubles; mocks, dummies, stubs, etc.
  • Better framework support for partials, etc.
  • Auto-creation of test subject - if constructor takes fields that are mocked out in the test can auto-create the test subject.
  • Auto-injection of Spring/Guice/Pico beans.
  • Auto-creation of arrays filled with mocks.
  • Auto triangulation - Framework takes care of creating (sensible) random values in order to drive out correct code.
  • ...?

Downsides

  • Lack of explicitness - as you don't have the code in every test, need to go hunting for how things are created.
  • Investment in understanding the mocking infrastructure; lifecycle, etc.
  • Need to roll your own or use a non-"standard" framework such as Instict.