Unit tests with EasyMock

Let’s write unit tests for the REST service implementation. We want to test this component isolatedly. One manner to achieve that is to use a mocking framework, such as EasyMock, Mockito or PowerMock.

The complete source code is available here.

Technologies used :

  • Java SE Development Kit 8u66
  • Eclipse IDE for Java Developers Version: Mars.1 Release (4.5.1)
  • Maven 3.3.3 (comes with Eclipse)
  • EasyMock 3.4 (as Maven dependency)

pom.xml

<dependency>
  <groupId>org.easymock</groupId>
  <artifactId>easymock</artifactId>
  <version>3.4</version>
  <scope>test</scope>
</dependency>

Unit test

We will test all the methods of the REST service implementation. We always start with the shortest path and build up on it. You can use a code coverage tool (EclEmma, eCobertura, …) to make sure you go through all the code.

The tests are run by the EasyMockRunner.

We use annotations @Mock and @TestSubject to initialize and inject mocks.

Generally speaking you first need to describe the expected behaviour of the mock (method called, expected parameters, returned answer or exception), then you put it in a replay state, after that you call the method you want to test and eventually you verify everything has been called as expected.

By default, argument expectations are verified using the equals() method. EasyMock provides a lot of predefined matchers to perform other verifications (class instance, regexp, …). We will write our own matcher for the PersonEntity class, as we do not want to rely on its implementation of the equals method.

@RunWith(EasyMockRunner.class)
public class PersonServiceImplTest {

    private static final String LAST_NAME = "Doe";
    private static final String FIRST_NAME = "John";
    private static final String OTHER_LAST_NAME = "Musterman";
    private static final String OTHER_FIRST_NAME = "Max";
    private static final long ID = 1234L;
    private static final String NFE_ID = "abc";
    private static final long NOT_FOUND_ID = 5678L;

    @TestSubject
    private PersonServiceImpl sut = new PersonServiceImpl();

    @Mock
    private PersonRepository repository;

    @Test
    public void testGetAllEmptyResult() {
        // given
        expect(repository.findAll()).andReturn(Collections.emptyList());
        replay(repository);

        // when
        Collection<PersonResource> res = sut.getAll();

        // then
        Assert.assertNotNull(res);
        Assert.assertTrue(res.isEmpty());
        verify(repository);
    }

    @Test
    public void testGetAll() {
        // given
        expect(repository.findAll()).andReturn(Arrays.asList(createPersistedPersonEntity()));
        replay(repository);

        // when
        Collection<PersonResource> res = sut.getAll();

        // then
        Assert.assertNotNull(res);
        Assert.assertEquals(1, res.size());
        checkPerson(res.iterator().next());
        verify(repository);
    }

    @Test(expected = NumberFormatException.class)
    public void testGetNumberFormatException() throws PersonNotFound {
        // given

        // when
        sut.get(NFE_ID);

        // then
    }

    @Test(expected = PersonNotFound.class)
    public void testGetPersonNotFound() throws PersonNotFound {
        // given
        expect(repository.findOne(NOT_FOUND_ID)).andReturn(null);
        replay(repository);

        // when
        try {
            sut.get("" + NOT_FOUND_ID);
        } finally {
            // then
            verify(repository);
        }
    }

    @Test
    public void testGet() throws PersonNotFound {
        // given
        expect(repository.findOne(ID)).andReturn(createPersistedPersonEntity());
        replay(repository);

        // when
        PersonResource res = sut.get("" + ID);

        // then
        checkPerson(res);
        verify(repository);
    }

    @Test
    public void testCreate() {
        // given
        expect(repository.save(eqPersonEntity(createPersonEntity()))).andReturn(createPersistedPersonEntity());
        replay(repository);

        // when
        PersonResource res = sut.create(createPersonResource());

        // then
        checkPerson(res);
        verify(repository);
    }

    @Test(expected = NumberFormatException.class)
    public void testUpdateNumberFormatException() throws PersonNotFound {
        // given

        // when
        sut.update(NFE_ID, createPersonResource());

        // then
    }

    @Test(expected = PersonNotFound.class)
    public void testUpdatePersonNotFound() throws PersonNotFound {
        // given
        expect(repository.findOne(NOT_FOUND_ID)).andReturn(null);
        replay(repository);

        // when
        try {
            sut.update("" + NOT_FOUND_ID, createPersonResource());
        } finally {
            // then
            verify(repository);
        }
    }

    @Test
    public void testUpdate() throws PersonNotFound {
        // given
        expect(repository.findOne(ID)).andReturn(createPersistedPersonEntity());
        expect(repository.save(eqPersonEntity(createOtherPersistedPersonEntity())))
                .andReturn(createOtherPersistedPersonEntity());
        replay(repository);

        // when
        PersonResource res = sut.update("" + ID, createOtherPersonResource());

        // then
        checkOtherPerson(res);
        verify(repository);
    }

    @Test(expected = NumberFormatException.class)
    public void testDeleteNumberFormatException() {
        // given

        // when
        sut.delete(NFE_ID);

        // then
    }

    @Test
    public void testDelete() {
        // given
        repository.delete(ID);
        replay(repository);

        // when
        sut.delete("" + ID);

        // then
        verify(repository);
    }

    protected PersonEntity createPersonEntity() {
        return new PersonEntity(FIRST_NAME, LAST_NAME);
    }

    protected PersonEntity createPersistedPersonEntity() {
        PersonEntity person = createPersonEntity();
        person.setId(ID);
        return person;
    }

    protected PersonEntity createOtherPersistedPersonEntity() {
        PersonEntity person = new PersonEntity(OTHER_FIRST_NAME, OTHER_LAST_NAME);
        person.setId(ID);
        return person;
    }

    protected PersonResource createPersonResource() {
        PersonResource person = new PersonResource();
        person.setFirstName(FIRST_NAME);
        person.setLastName(LAST_NAME);
        return person;
    }

    protected PersonResource createOtherPersonResource() {
        PersonResource person = new PersonResource();
        person.setFirstName(OTHER_FIRST_NAME);
        person.setLastName(OTHER_LAST_NAME);
        return person;
    }

    protected void checkPerson(PersonResource person) {
        Assert.assertNotNull(person);
        Assert.assertEquals("" + ID, person.getId());
        Assert.assertEquals(FIRST_NAME, person.getFirstName());
        Assert.assertEquals(LAST_NAME, person.getLastName());
    }

    protected void checkOtherPerson(PersonResource person) {
        Assert.assertNotNull(person);
        Assert.assertEquals("" + ID, person.getId());
        Assert.assertEquals(OTHER_FIRST_NAME, person.getFirstName());
        Assert.assertEquals(OTHER_LAST_NAME, person.getLastName());
    }

    public static PersonEntity eqPersonEntity(PersonEntity in) {
        reportMatcher(new PersonEntityEquals(in));
        return null;
    }

}

Here is the argument matcher.

public class PersonEntityEquals implements IArgumentMatcher {

    private final PersonEntity expected;

    public PersonEntityEquals(PersonEntity expected) {
        this.expected = expected;
    }

    @Override
    public void appendTo(StringBuffer buffer) {
        buffer.append("eqPersonEntity(");
        buffer.append(expected);
        buffer.append(")");
    }

    @Override
    public boolean matches(Object actual) {
        if (!(actual instanceof PersonEntity)) {
            return false;
        }
        PersonEntity other = (PersonEntity) actual;
        String firstName = expected.getFirstName();
        if (firstName == null) {
            if (other.getFirstName() != null)
                return false;
        } else if (!firstName.equals(other.getFirstName()))
            return false;
        Long id = expected.getId();
        if (id == null) {
            if (other.getId() != null)
                return false;
        } else if (!id.equals(other.getId()))
            return false;
        String lastName = expected.getLastName();
        if (lastName == null) {
            if (other.getLastName() != null)
                return false;
        } else if (!lastName.equals(other.getLastName()))
            return false;
        return true;
    }

}

Launch

In the rest module run :

mvn clean install

Output

[INFO] --- maven-surefire-plugin:2.19:test (default-test) @ rest ---

-------------------------------------------------------
 T E S T S
-------------------------------------------------------
Running de.griesser.rest.services.PersonServiceImplTest
Tests run: 11, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.029 sec - in de.griesser.rest.services.PersonServiceImplTest

Results :

Tests run: 11, Failures: 0, Errors: 0, Skipped: 0
Written on December 28, 2015