http://www.developer.com/java/other/article.php/3882311/Mockito-Java-Unit-Testing-with-Mock-Objects.htm
One of the hallmarks of an experienced developer is an appreciation of the importance of unit and integration testing. Although integration testing is the ultimate way to verify application functionality, it does come with a lot of startup/execution/shutdown overhead. Sometimes it is easier to verify particular application components in isolation, without the actual application running. In this article, I show how you can still thoroughly test components that would usually require integration testing using regular, basic JUnit tests. Using code samples, I will walk you through writing unit tests for a JAX-RS REST Web service that fetches Person data from a database and returns it to the caller. I will demonstrate how to write a JUnit unit test for such service -- without having to actually deploy it into a running Web container. You will mock out the Hibernate DAO to simulate interaction with a database. Any "live" business logic component relies on interactions with other components. It may be a POJO with some additional business logic, a Hibernate/JPA DAO returning some entities from the database, or an HTTP call (via REST or SOAP) to an external system. Mock objects allow us to mock out these dependencies. Using standard dependency injection (regardless of whether you are using Java EE, Spring or Guice), you can then inject those mocked objects into your business logic component and verify its interactions. Mock objects are one of the biggest payoffs that come with adopting dependency injection as the core of your application's architecture. Java is perfectly suited for providing this functionality, thanks to its capabilities for runtime bytecode manipulation and object proxy generation. In fact, quite a few mock object libraries are available in the Java ecosystem (EasyMock, for example), but my preference has been to use Mockito. It has an incredibly easy and logical API and is under very active development. You can download it manually from the Mockito website, or if you use Maven/Ivy/Grape, just add it as a test-scoped dependency. (At the time of writing, the latest version was 1.8.4.) You will be testing the following simple JAX-RS REST service, deployed as a Spring bean. Note that the attached code samples are a regular Maven project. Just execute Testing a Web service implies that you have to deploy it. But with mock objects, that assumption is incorrect. In all your JUnit test classes, make sure you do a static import of the Mockito library: Then you can simply use the Let's ensure that when the REST service's The Suppose you want to test the REST service, knowing the Here's another sample: suppose you want to ensure that an HTTP code 200 (OK) is returned when the primary key you passed exists in the database, but a 404 is returned if it does not. With Mockito, a test is trivial: Because hitting the database is an expensive operation, you should ensure that the REST service calls the DAO only once per service call. Using Mockito's Suppose that when a DAO operation fails (e.g., database is down) the service's I have only scratched the surface of what Mockito can do. Check out their detailed documentation for more advanced samples. Understanding the importance of unit testing (and being able to use mock objects to accomplish it) is one of the critical lessons of becoming a senior Java developer. It will surely enhance your standing in a competitive job market.
Mockito: Java Unit Testing with Mock Objects
May 14, 2010
Unit Tests, Mock Objects and Mockito
JAX-RS Service Code Sample
mvn test or mvn integration-test to run unit tests and integration tests, respectively. You should be able to open it in any modern Java IDE. @Service("personService")
@Path(PersonService.PATH)
public class PersonService {
public static final String PATH = "/person";
private PersonDao dao;
@Autowired public void setPersonDao(PersonDao dao) {
this.dao = dao;
}
@GET
public List findAll() {
List people = dao.findAll();
return (people.size() > 0) ? people : null;
}
@GET @Path("{id}")
public Response find(@PathParam("id") Integer id) {
Person p = dao.findByPrimaryKey(id);
if (p != null) {
return Response.ok(p).build();
} else {
return Response.status(Status.NOT_FOUND).build();
}
}
@POST
public Response add(@FormParam("firstName") String firstName,
@FormParam("lastName") String lastName,
@FormParam("email") String email) {
try {
Person person = new Person();
person.setFirstName(firstName);
person.setLastName(lastName);
person.setEmail(email);
Integer id = dao.save(person);
return Response.created(UriBuilder.fromPath(PATH +
"/{0}").build(id)).entity(person).build();
} catch (Exception ex) {
return Response.serverError().build();
}
}
@PUT @Path("/{id}")
public Response update(@PathParam("id") Integer id,
@FormParam("firstName") String firstName,
@FormParam("lastName") String lastName,
@FormParam("email") String email) {
try {
Person person = dao.findByPrimaryKey(id);
if (person != null) {
if (firstName != null) {
person.setFirstName(firstName);
}
if (lastName != null){
person.setLastName(lastName);
}
if (email != null) {
person.setEmail(email);
}
dao.update(person);
return Response.ok(person).build();
} else {
return Response.noContent().build();
}
} catch (Exception e) {
return Response.serverError().build();
}
}
@DELETE
public Response deleteAll() {
try {
dao.deleteAll();
return Response.ok().build();
} catch (Exception e) {
return Response.serverError().build();
}
}
@DELETE @Path("/{id}")
public Response delete(@PathParam("id") Integer id) {
try {
dao.deleteByPrimaryKey(id);
return Response.ok().build();
} catch (Exception e) {
return Response.serverError().build();
}
}
}
Creating Mock Objects
import static org.mockito.Mockito.*;
mock() method to create mock object instances. public class PersonServiceMockTest {
private PersonService service;
private PersonDao dao; //we will be mocking this class
@Before
public void setup() {
dao = mock(PersonDao.class); //here is the actual mocking call
service = new PersonService();
service.setPersonDao(dao);
}
Verify Methods Were Called
findAll() method is called, the corresponding DAO findAll() method is called as well. This is accomplished using intuitive Mockito methods such as when(*method*).thenReturn(*value*):@Test
public void testFindAll() {
List all = new LinkedList();
all.add(new Person(1,"John","Doe",null));
all.add(new Person(2,"Jane","Doe",null));
//MOCK ALERT: return mocked result set on find
when(dao.findAll()).thenReturn(all);
//call the main method you want to test
List result = service.findAll();
//MOCK ALERT: verify the method was called
verify(dao).findAll();
}
verify() method is used to verify that the mocked out method was actually called. Test Behavior with Different Return Values from Methods
findAll() method will return a null if no data is found in the database. You can do this easily by telling the mocked DAO to return an empty collection. @Test
public void testNullReturnIfNoDataFound() {
List all = new LinkedList();
//return mocked result set on find
when(dao.findAll()).thenReturn(all);
//call the main method you want to test
List result = service.findAll();
//verify the method was called
verify(dao).findAll();
//verify null result was returned
assertEquals(null,result);
}
@Test
public void testResponsesOnFind() {
//simulate Person ID = 1 in the DB
when(dao.findByPrimaryKey(1)).thenReturn(new Person(1,"John","Doe",null));
//test response when ID exists
Response found = service.find(1);
assertEquals(200, found.getStatus());
//test response when ID does not exist in DB
Response notFound = service.find(999);
assertEquals(404, notFound.getStatus());
}
Verify Number of Times Method Was Called
times(), atMost(), and atLeast() methods, you can test exactly how many times an expensive operation is called: @Test
public void testDaoCalledOnlyOnce() {
List all = new LinkedList();
//return mocked result set on find
when(dao.findAll()).thenReturn(all);
//call the main method you want to test
service.findAll();
//verify the method was called exactly once
verify(dao,times(1)).findAll();
verify(dao, atMost(1)).findAll();
verify(dao, atLeast(1)).findAll();
}
Test Error Handling
update() method will not propagate the exception to the client. Instead, it will return a clean JAX-RS Response object with a regular HTTP error code 500. You can simulate this behavior by telling the mock DAO to throw a RuntimeException when it gets called and verifying how the JAX-RS Web service handles it: @Test
public void testServerErrorReturnedOnFailedUpdate() {
when(dao.findByPrimaryKey(1)).thenThrow(new RuntimeException("DB Failed"));
Response r = service.update(1, "John","Doe","john.doe@gmail.com");
assertEquals(500,r.getStatus());
}
Summary
Code Download
About the Author
Jacek Furmankiewicz is a Senior Java EE, Python, Oracle and MySQL developer at Radialpoint. He has 16 years of IT experience in writing enterprise software for a wide variety of industries.