Difference between revisions of "Simplifying Mock Object Testing"
(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 15: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.