All 3 entries tagged Jmock
No other Warwick Blogs use the tag Jmock on entries | View entries tagged Jmock at Technorati | There are no images tagged Jmock on this blog
February 01, 2006
JMock lies :)
So the excellent JMock library contains a constraint called ANYTHING which should, accept any object.
Unfortunately it doesnt. It falls if you provide an enum.
Minor point; but rather unintuitive ;)
October 11, 2005
jmock versus easymock (a trivial example)
So I decided to have a look at easymock.
It looks very nice. Some of the main benefits include:
- direct method calls, so refactoring works
- less overhead required because you are manipulating your objects, not mocks
For example, there is an object call RequestScopeModelAccessor which stores/retrieves things under FlowScope within a web flow.
Using jmock, the get method can be tested:
public void testGet() {
String key = "key";
Object obj = "object";
// set up for writing
Scope requestScope = new Scope(ScopeType.REQUEST);
requestScope.setAttribute(key, obj);
Mock context = mock(RequestContext.class);
context.expects(atLeastOnce()).method("getRequestScope").will(returnValue(requestScope));
RequestScopeModelAccessor accessor = new RequestScopeModelAccessor(key);
assertEquals("get", obj, accessor.get((RequestContext) context.proxy()));
}
whereas with easymock:
public void testGet() {
String key = "key";
Object obj = "object";
// set up for writing
Scope requestScope = new Scope(ScopeType.REQUEST);
requestScope.setAttribute(key, obj);
RequestContext requestContext = createMock(RequestContext.class);
requestContext.getRequestScope();
expectLastCall().andReturn(requestScope);
replay(requestContext);
RequestScopeModelAccessor accessor = new RequestScopeModelAccessor(key);
assertEquals("get", obj, accessor.get(requestContext));
}
As you can see, the code is much easier to read, and the mock framework is much less intrusive with easymock.
This of course is a very simplistic example, but it has encouraged me to look a bit deeper :)
BTW: I am using prerelease 2.0 which will only work with jdk 5.
Unit testing using mock objects
So anybody who has worked with me knows that I am a huge fan of unit testing, and it is not unusual for the amount of unit test code to exceed the amount of code being tested.
Looking back, it appears that most of my unit tests are mainly transparent (white box) and I prefer mocks over stubs.
This was never really a conscious decision, but I am quite comfortable with it. The benefits of white box testing is that you know your code is doing exactly the right thing. For example, the code in Sitebuilder2 that generates the object graph for the site navigation is very expensive, and therefore it is critical that it does the right thing. For example, Page has a method "isPubliclyVisible", which is a simple boolean field. It is essential (for performance) that this is called before doing an expensive webgroups lookup, and this logic is built into the unit tests. Were I to do a black box approach, it could be argued that this logic is absolutely internal and should not be tested.
In order to define these restrictions, I use link to define mocks for any collaborators. Conceptually this is the right thing to do. Stubs would not give me the control I required. On the other hand, a lot more work is required to define your mock objects. JMock also falls down when it comes to refactoring because you describe all your methods using strings.
When would I use stubs? Well, to be honest, very rarely. When should I use stubs? I would recommend using stubs when you truly do not care about how your stub will be used.
One of the unfortunate asides of using jmock to explictly define the allowed method calls is that you can very easily end up having to modify the expectations on objects just because you change a seemingly independant line of code.
For example, assume I have a method on page called addPage(Page). Imagine I know have a unit test for the duplicate method:
public void testSomething(){
// some code….
Mock mockPage = mock(Page.class);
Page page = (Page) mockPage.proxy();
HtmlPage sourcePage = new HtmlPageImpl();
sourcePage.addPage(page);
}
If I change the Page.addPage(Page) method to automatically set the site, i.e.:
public void addPage(final Page page){
getPages().add(page);
page.setSite(getSite());
}
then all of a sudden, anywhere I have mocked up a Page which is added to another page will fail. This is a horrible, but unavoidable consequence of using mock objects for which you are expliclty defining behaviour.
Had I used a stub to represent Page, no problem.
So I do firmly believe that mock objects have an absolutely invaluable place, but they do need to be used with care.
If you care about how your object will be used, use mocks, if you don't, use stubs. Are there mock object frameworks which provide a bit more flexibility; yes, easymock looks quite promising (link).
Will I learn, and start using stubs, probably not :)