Friday, October 8, 2010

Mockito Sample

I am one of those Developers who "steals" the code from the web. But in this case I write my own example, because I have been told so...

Mockito is a mock object framework for Java. You can find it here.  The following example shows how to mock up the dependencies for your system under JUnit test. In this particular example, I'm testing an InventoryService class and it has a dependency to a BookDao interface.  Using interface-based programming makes it easy to swap in different implementations of a supertype.  First we have object Book - simple POJO:

package example1.model;

public class Book {
  
     private int id;
     private String name;
     private String author;
     private int year;
    
     public int getId() {
      return id;
     }
     public void setId(int id) {
      this.id = id;
     }
     public String getName() {
      return name;
     }
     public void setName(String name) {
      this.name = name;
     }
    
     public String getAuthor() {
      return author;
     }
     public void setAuthor(String author) {
      this.author = author;
     }
         
     public void setYear(int year) {
      this.year = year;
     }
    
     public int getYear() {
          return year;
         }
}

Second, the interface types for BookInventoryService 

package example1.service;

import java.util.List;

import example1.dao.BookDao;
import example1.model.Book;

public interface BookInventoryService {
   
    public void setDataAccess(BookDao bookDao);
    public List<Book> getInventory();
    public Book getInventoryById(int id) throws Exception;
    public void updateInventory(List<Book> books);
    public void deleteInventory(List<Book> books);
   
}

and BookDao:

package example1.dao;

import java.util.List;

import example1.model.Book;

public interface BookDao {
    public Book getBookById(int id);
    public List <Book> getBooks();
    public void updateBook(Book book);
    public void deleteBook(Book book);
}

Next up is the implementation of BookInventory - MysteryBookInventory class, which will be tested.
The MysteryBookInventory collaborates with a BookDao type, but it does not take on the responsibility of creating the BookDao implementation.  That responsibility lies elsewhere in the application.  In this scenario, the unit test will provide the implementation (a mock implementation using Mockito) and inject that instance into the MysteryBookInventory instance.

package example1.service;

import java.util.List;

import example1.dao.BookDao;
import example1.model.Book;

public class MysteryBookInventoryService implements BookInventoryService{
   
    BookDao bookDao;
   
    @Override
    public void setDataAccess(BookDao bookDao) {
        this.bookDao = bookDao;
    }

    @Override
    public List<Book> getInventory() {
        return bookDao.getBooks();
    }

    @Override
    public Book getInventoryById(int id) throws Exception {
        Book book = bookDao.getBookById(id);
        if (book == null) {
            throw new Exception("Book not found.");
        }
        return book;
    }
   
    @Override
    public void updateInventory(List<Book> books) {
        for (Book book : books) {
            bookDao.updateBook(book);
        }
    }

    @Override
    public void deleteInventory(List<Book> books) {
        for (Object book : books) {
            bookDao.deleteBook((Book)book);
        }
    }

}

Finally the unit tests.  Mockito is used to create mock instances of the BookDao type, suitable for testing the BookInventoryService implementation. I am using @Mock Annotation. You can import it through RunWith JUnit statement or you will have to import it as a package at the beginning. I have 3 tests set up: 1) verification of a state and of a method call; 2) verifying the Exception thrown and 3) is consecutive stubbing:

import static org.junit.Assert.*;
import static org.mockito.Mockito.when;

import java.util.*;

import example1.dao.BookDao;
import example1.model.Book;
import example1.service.BookInventoryService;
import example1.service.MysteryBookInventoryService;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.runners.MockitoJUnitRunner;
import static org.mockito.Mockito.verify;

@RunWith(MockitoJUnitRunner.class)
public class InventoryServiceTest {
   
    BookInventoryService service;
   
    @Mock
    private BookDao bookDao;
   
    @Mock
    private List <Book> mockedBooks;
   
    @Mock
    private Book mockedBook1;
   
    @Mock
    private Book mockedBook2;
   
    @Before
    public void setUp() throws Exception {
        MockitoAnnotations.initMocks(this); //Important! Mocks need to be initialized.
        service = new MysteryBookInventoryService();
        service.setDataAccess(bookDao);
    }
   
    //verifying state and behavior
    @Test
    public void testGetInventory() {
        when(bookDao.getBooks()).thenReturn(mockedBooks);
        List <Book> resultList = service.getInventory();
        //verify state
        assertNotNull("Inventory should not be null", resultList);
        //verify behavior
        verify(bookDao).getBooks();
    }
   
    //verifying Exception
    @Test(expected = Exception.class)
    public void testGetInventoryById() throws Exception {
        when(bookDao.getBookById(1)).thenReturn(null);
        @SuppressWarnings("unused")
        Book book = service.getInventoryById(1);
    }
   
    //Consecutive stubbing
    @Test
    public void testGetInventoryByIdManyTimes() throws Exception{
        when(bookDao.getBookById(1)).thenReturn(mockedBook1, mockedBook2);
        Book result = service.getInventoryById(1);
        //verify 1 state
        assertEquals(result,mockedBook1);
       
        result = service.getInventoryById(1);
        //verify 2 state
        assertEquals(result,mockedBook2);
    }
}

1 comment:

  1. Great first post. Very helpful. Check out this syntax highlighter... it will make things a lot easier any time you need to post code..

    http://alexgorbatchev.com/SyntaxHighlighter/

    ReplyDelete