AppFuseEasyMock |
|
Your trail: |
This is version 3.
It is not the current version, and thus it cannot be edited.
[Back to current version]
[Restore this version]
Simplify you test using EasyMock
This articel will give a short overview how to integrate EasyMock in AppFuse.
For further questions you can contact me at josip(at)esistegalaber(dot)de
I assume that you have a running AppFuse project:
- Download a suitable version of EasyMock
- Integrate the libraries in AppFuse
- Write some Tests using EasyMock
- Run the Tests and have fun.
- Change all AppFuse (1.9) Service tests to easymock (optional)
- Take a moment to think of what is happening...
Download a suitable version of EasyMock
- The latest version of Easymock is 2.0 but this version works only with Java 2 Version 5.0 and above. As I use Java 1.4x the rest of this tutorial is based on EasyMock 1.2.
Integrate the libraries in AppFuse
- Create a new folder named 'easymock-1.2' int the /lib directory of your AppFuse project.
- Copy easymock.jar in the new directory
- Add the following part to the lib/lib.properties file:
#
# EasyMock - http://www.easymock.com
#
easymock.version=1.2
easymock.dir=${lib.dir}/easymock-${easymock.version}
easymock.jar=${easymock.dir}/easymock.jar
|
- Finally you have to add the reference to the easymock.jar to the service test classpath (or whatever module you want to mock). In order to do this, you have to edit your properties.xml file and add the following element to your service.test.classpath:
So the complete service.test.classpath would look like
<path id="service.test.classpath">
<path refid="service.compile.classpath"/>
<pathelement location="${junit.jar}"/>
<pathelement location="${log4j.jar}"/>
<pathelement location="${log4j.jar}"/>
<fileset dir="${servletapi.dir}" includes="*.jar"/>
<pathelement location="${jakarta-oro.jar}"/>
<pathelement location="${dumbster.jar}"/>
<pathelement location="${jmock.jar}"/>
<pathelement location="${easymock.jar}"/>
<path location="test/dao"/>
</path>
- If you are using Eclipse, you also have to add the easymock.jar to your buildpath (Project properties -> java buildpath -> jar)
Now you are set up to write some tests with EasyMock.
Write the tests.
Basically I will rewrite the PeronManagerTest known from the Appfuse tutorial. Using EasyMock the Testcase will be set up like this:
package org.appfuse.service;
import org.appfuse.dao.PersonDao;
import org.appfuse.model.Person;
import org.appfuse.service.impl.PersonManagerImpl;
import org.easymock.MockControl;
import org.springframework.orm.ObjectRetrievalFailureException;
public class PersonManagerTest extends BaseManagerTestCase
{
//create a PersonManager instance to be tested.
private PersonManager personManager = new PersonManagerImpl();
//create a MockControl-object to retrieve and control a mock.
private MockControl personDaoControl = MockControl.createStrictControl(PersonDao.class);
//create an mocked instance of the PersonDao the manager depends on.
private PersonDao personDao = (PersonDao) personDAOControl.getMock();
/**
* @see junit.framework.TestCase#setUp()
*/
protected void setUp() throws Exception
{
super.setUp();
//inject the mocked DAO into the Manager
personManager.setPersonDao(personDao);
}
/**
* @see junit.framework.TestCase#tearDown()
*/
protected void tearDown() throws Exception
{
super.tearDown();
//clean up.
personDao = null;
personDaoControl = null;
personManager = null;
}
}
|
The difference to Mocktests known from the tutorial is simply that we don't use the Mock themselves but an MockControl that will ease the use of the actual Mock. In this case I use the static Method MockControl.createStrictControl(PersonDao.class); that will create a MockControl object that will take care of ordering of methodcalls to the Mocked object. Let's take a look at the actual TestMethods:
/*
* Test method for 'com.bomber.service.impl.PersonManagerImpl.getPerson(String)'
*/
public void testGetPerson()
{
//create testdata
Person person = new Person();
person.setId(new Long(1));
person.setFirstName("Matt");
person.setLastName("Raible");
//set expectations
personDao.getPerson(new Long(1));
personDaoControl.setReturnValue(person);
//call to the method to be tested
personDaoControl.replay(); //set the mockcontrol to replay-state
Person testPerson = personManager.getPerson("1"); //actual methodcall
assertEquals("Wrong person retrieved!", person, testPerson); //test the returned data
personDaoControl.verify(); //verify the mockobject
}
/*
* Test method for 'com.bomber.service.impl.PersonManagerImpl.removePerson(String)'
*/
public void testRemovePerson()
{
//set expectations
personDao.removePerson(new Long(1));
personDaoControl.replay(); //set the mockcontrol to replay-state
personManager.removePerson("1");
personDaoControl.verify(); //verify the mockobject
}
/*
* Test method for 'com.bomber.service.impl.PersonManagerImpl.savePerson(Person)'
*/
public void testSavePerson()
{
//create testdata
Person person = new Person();
person.setId(new Long(1));
person.setFirstName("Matt");
person.setLastName("Raible");
//set expectations
personDao.savePerson(person);
//test the methodcall
personDaoControl.replay(); //set the mockcontrol to replay-state
personManager.savePerson(person);
personDaoControl.verify(); //verify the mockobject
}
/*
* Test method for 'com.bomber.service.impl.PersonManagerImpl.getPerson(Person)'
*/
public void testGetPersonThatDoesntExist()
{
//set expectations
personDao.getPerson(new Long(1));
//tell the mock to throw an exception
personDaoControl.setThrowable(new ObjectRetrievalFailureException(Person.class, new Long(1)));
//set the MockControl to replay-state
personDaoControl.replay();
try
{
personManager.getPerson("1");
fail("Excpected Exception not thrown!");
}
catch (Exception e)
{
assertNotNull(e);
personDaoControl.verify();
}
}
|
I think this form of mocking your test is very simple to understand. The MockControl simply keeps track of the calls you make to the mocked instance. This way it becomes very easy to write down your expectations and the rather complicated setup of expectaions using bare Mocks is not needed anymore. Just like with bare Mocks you can set (void) return values or exceptions to be thrown. There are even convenience methods to specify that a method i supposed to be called n times. For more information on what EasyMock can do for you visit the EasyMock Documentation.
Run the tests
All that is left to do is run the test. Either execute ant test-service or call the single testcase by executing
ant test-service -Dtestcase=PersonManager.
Hopefully I haven't misstyped anything and you will see a BUILD SUCCESSFULL
Yeah Baby, Yeah!
Have fun!
Change all AppFuse (1.9) Service tests to easymock
Download the RAR file and extract all tests to your test/service folder. Maybe you have to change the package names...
Some further thougts.
I consider Mocks to be a bit more than just a means of White Box Tests. As the term itself indicates you need to have some knowlegde of the tested code in order to write a meaningful test. In contrast to Black box Tests where you don't care about the code itself, but only about the outcome of the test it will give you some information about the code you wrote. So, as a rule of thumb I belive that if you have difficulties writing a mock test for you business object that simply means your business object is designed badly. Therefore my paradigm concerning mock tests is simply: "If you can't test it - refactor it!". Comments appreciated!
|