Showing posts with label Testing. Show all posts
Showing posts with label Testing. Show all posts

Wednesday, 7 December 2016

[Java / Assertj] How to create your own fluent assertions ?

Everyone should write unit tests. And I mean EVERYONE. Integration tests are very popular and useful but unit tests are the fastest. Having good unit tests allows you to check whether your class works within second or two. UTs also tell you that something's wrong but how they do it ? When test fails you know that some parts of your code don't work as you expect. You see the name of failing test and a class which the test corresponds to so you have general idea what might have gone wrong but if you want to fix that bug you have to read both test and the code. This is why your tests should be extremely readable. I use the following live template for all the tests I write (Intellij Idea):
@Test
public void $METHOD_NAME$() throws Exception {
    // given
    $END$
    
    // when
    
    
    // then
    
}
When section invokes a method which is being tested by particular test. Given section prepares the input and records mocks' behaviour. Then section checks the output. I really like given-when-then template. It clearly separates those three sections so you always know which object is the input and so on. When section typically contains single line of code. Actually I like when each section is one-liner but it's usually impossible. Perfect test for me looks basically like that:
@Test
public void should_calculate_ceiling() throws Exception {
    // given
    double price = 7.8;
    
    // when
    double ceiling = Math.ceil(price);

    // then
    assertThat(ceiling).isEqualTo(8.0);
}
That case is actually too simple because typically you have to check the state of some object. Let's say that you're testing some class that returns an instance of Person:
@AllArgsConstructor
@Getter
public static class Person {
    private final String firstName;
    private final String lastName;
    private final int age;
    private final Sex sex;
    private final Optional<Job> job;

    public static enum Sex {
        MALE, FEMALE
    }

    public static class Job {}
}
I would never implement Person class like this but it's just an example so forgive me that :) If you use standard junit assertions you would probably write something like that:
@Test
public void should_return_joey() throws Exception {
    // when
    Person person = p();

    // then
    assertEquals(Sex.MALE, person.getSex());
    assertEquals("Joey", person.getFirstName());
    assertEquals("Tribiani", person.getLastName());
    assertTrue(person.getAge() > 18);
    assertEquals(Optional.empty(), person.getJob());
}

private Person p() {
    return new Person("Joey", "Tribiani", 25, Sex.MALE, Optional.empty());
}
I really don't like assertEquals(). First of all you have to remember order of parameters. The first one is expected and the second actual. If you make mistake you will see a misleading message. Another thing assertTrue() doesn't tell you why Joey has to be older than 18. This is why you should use some library that contains fluent assertions and allows you to create your own assertions. The most popular assertions library for Java is probably AssertJ. Having for instance a string you can check many things at once:
assertThat("Joey Tribiani").hasSize(13)
                           .startsWith("Jo")
                           .contains("y T")
                           .endsWith("ani")
                           .doesNotContain("Rachel");
Same applies to collections:
List<String> friends = ImmutableList.of("Rachel", "Ross", "Joey", "Chandler", "Pheebs", "Monica");
assertThat(friends).hasSize(6)
                   .doesNotContain("Gunther")
                   .containsSequence("Rachel", "Ross")
                   .endsWith("Monica")
                   .allMatch(friend -> friend.matches("[A-Z][a-z]*"));
And so on... Ok let's write custom assertions for Person class. First of all you have to create a class that extends AbstractAssert
public class PersonAssertions extends AbstractAssert<PersonAssertions, Person> {
}
Then we must add constructor that matches super() and static method assertThat:
public class PersonAssertions extends AbstractAssert<PersonAssertions, Person> {
    public static PersonAssertions assertThat(final Person actual) {
        return new PersonAssertions(actual);
    }

    private PersonAssertions(final Person actual) {
        super(actual, PersonAssertions.class);
    }
}
Now we can start adding assertions. First of all let's create some methods that allow you to check first and last name.
public PersonAssertions hasFirstName(final String name) {
    Assertions.assertThat(name).isEqualTo(actual.getFirstName());
    return this;
}

public PersonAssertions hasLastName(final String lastName) {
    Assertions.assertThat(lastName).isEqualTo(actual.getLastName());
    return this;
}
Note that we always return this to make the assertions fluent. Now:
assertEquals("Joey", person.getFirstName());
assertEquals("Tribiani", person.getLastName());
Can be replaced by:
assertThat(person).hasFirstName("Joey")
                  .hasLastName("Tribiani");
Now let's check gender:
public PersonAssertions isFemale() {
    Assertions.assertThat(actual.getSex()).isSameAs(Sex.FEMALE);
    return this;
}

public PersonAssertions isMale() {
    Assertions.assertThat(actual.getSex()).isSameAs(Sex.MALE);
    return this;
}
Now we have:
assertThat(person).hasFirstName("Joey")
                  .hasLastName("Tribiani")
                  .isMale();
Those assertions are just fancy methods that check internal state of given object. All the created methods are way more readable but we can still add more specific assertions that suits to our domain. Let's assume that we're selling alcohol.... In Poland you can buy alcohol if you're 18 years old.
public PersonAssertions canBuyBeerInPoland() {
    Assertions.assertThat(actual.getAge()).isGreaterThanOrEqualTo(18);
    return this;
}

assertThat(person).hasFirstName("Joey")
                  .hasLastName("Tribiani")
                  .isMale()
                  .canBuyBeerInPoland();
canBuyBeerInPoland() looks much better than assertTrue(person.getAge() > 18) because it uses our domain language. And the last one. Let's check whether given person is unemployed. Person class contains Optional<Job> so we have to check if it's empty or not:
public PersonAssertions isUnemployed() {
    Assertions.assertThat(actual.getJob()).isEmpty();
    return this;
}
Now you can compare junit assertions to our custom assertj assertions:
JUnit:
assertEquals(Sex.MALE, person.getSex());
assertEquals("Joey", person.getFirstName());
assertEquals("Tribiani", person.getLastName());
assertTrue(person.getAge() > 18);
assertEquals(Optional.empty(), person.getJob());
AssertJ:
assertThat(person).hasFirstName("Joey")
                  .hasLastName("Tribiani")
                  .isMale()
                  .canBuyBeerInPoland()
                  .isUnemployed();
It looks much better and you can reuse all those methods. You should also think about overriding error messages. We're still using the same instance of Person:
private Person p() {
    return new Person("Joey", "Tribiani", 25, Sex.MALE, Optional.empty());
}
The follwoing assertion:
assertThat(person).hasFirstName("Joeya");
generates error message like this:
org.junit.ComparisonFailure: 
Expected :"Joey"
Actual   :"Joeya"
You can always override error message like that:
public PersonAssertions hasFirstName(final String name) {
    Assertions.assertThat(name).overridingErrorMessage("Given Person [" + actual + "] has name " + actual.getFirstName() + ". Expected: " + name)
            .isEqualTo(actual.getFirstName());
    return this;
}
Now the same test generates:
java.lang.AssertionError: Given Person [DistributorSalesScheduler.Person(firstName=Joey, lastName=Tribiani, age=25, sex=MALE, job=Optional.empty)] has name Joey. Expected: Joeya
The whole class looks as follows:
public static class PersonAssertions extends AbstractAssert<PersonAssertions, Person> {
    public static PersonAssertions assertThat(final Person actual) {
        return new PersonAssertions(actual);
    }

    private PersonAssertions(final Person actual) {
        super(actual, PersonAssertions.class);
    }

    public PersonAssertions hasFirstName(final String name) {
        Assertions.assertThat(name).overridingErrorMessage("Given Person [" + actual + "] has name " + actual.getFirstName() + ". Expected: " + name)
                .isEqualTo(actual.getFirstName());
        return this;
    }

    public PersonAssertions hasLastName(final String lastName) {
        Assertions.assertThat(lastName).isEqualTo(actual.getLastName());
        return this;
    }

    public PersonAssertions canBuyBeerInPoland() {
        Assertions.assertThat(actual.getAge()).isGreaterThanOrEqualTo(18);
        return this;
    }

    public PersonAssertions isFemale() {
        Assertions.assertThat(actual.getSex()).isSameAs(Sex.FEMALE);
        return this;
    }

    public PersonAssertions isMale() {
        Assertions.assertThat(actual.getSex()).isSameAs(Sex.MALE);
        return this;
    }

    public PersonAssertions hasJob() {
        Assertions.assertThat(actual.getJob()).isPresent();
        return this;
    }

    public PersonAssertions isUnemployed() {
        Assertions.assertThat(actual.getJob()).isEmpty();
        return this;
    }
}
As you see writing custom assertions is very easy and makes tests more readable. All the assertions are reusable and can be unit tested. The project I've been working on contains module with all the utilities for tests so other modules can import the assertions. I'm always very happy when I see that someone's created new set of assertions for our domain objects :)

Wednesday, 10 February 2016

[Java8 / Spring / Test] How to test TransactionTemplate's execute() method ?

If your app uses Spring framework you may be familiar with either @Transactional or TransactionTemplate. Altough TransactionTemplate couples your app with Spring a lot of people use it. In most cases it's being injected into DAOs or some kind of AbstractDAO. DAO is an object which typically is tested by integration test which enables in-memory database. In most cases you won't need unit testing here but what if TransactionTemplate has been injected into some kind of service / transaction etc - in general a class which has to be unit tested ? There is one problem with TransactionTemplate - execute() method takes TransactionCallback as a parameter. This is how you would invoke it:
transactionTemplate.execute((s) -> propertyDao.persist(copyOf(toSave).withNewResourceId(accountId)));
If you mock TransactionTemplate then propertyDao.persist() will never be invoked. In my unit test PropertyDao is a mock so now I cannot use Mockito.verify() to check whether persist method has been invoked (it returns void).
private final PropertyDao propertyDao = mock(PropertyDao.class);
Let's see how execute() method has been implemented:
    @Override
    public <T> T execute(TransactionCallback<T> action) throws TransactionException {
        if (this.transactionManager instanceof CallbackPreferringPlatformTransactionManager) {
            return ((CallbackPreferringPlatformTransactionManager) this.transactionManager).execute(this, action);
        }
        else {
            TransactionStatus status = this.transactionManager.getTransaction(this);
            T result;
            try {
                result = action.doInTransaction(status);
            }
            catch (RuntimeException ex) {
                // Transactional code threw application exception -> rollback
                rollbackOnException(status, ex);
                throw ex;
            }
            catch (Error err) {
                // Transactional code threw error -> rollback
                rollbackOnException(status, err);
                throw err;
            }
            catch (Exception ex) {
                // Transactional code threw unexpected exception -> rollback
                rollbackOnException(status, ex);
                throw new UndeclaredThrowableException(ex, "TransactionCallback threw undeclared checked exception");
            }
            this.transactionManager.commit(status);
            return result;
        }
    }
The most important line:
result = action.doInTransaction(status);
It simply means that our function:
transactionTemplate.execute((s) -> propertyDao.persist(copyOf(toSave).withNewResourceId(accountId)));
is being invoked in the method so when you mocked the template it won't happen at all. How to deal with that ? My first thought was to use ArgumentCaptor to catch the parameter passed to execute method and invoke it but I think I found a better way.
class FunctionCallingTransactionTemplate extends TransactionTemplate {
        @Override public <T> T execute(TransactionCallback<T> action) throws TransactionException {
            final TransactionStatus irrelevantStatus = null;
            return action.doInTransaction(irrelevantStatus);
        }
    }
In the code above I extend TransactionTemplate so that in only invokes the action passed to execute() method without other stuff. I guess I'm gonna need this in many tests so we can create a simple trait:
public interface FunctionCallingTransactionTemplateTrait {
    default TransactionTemplate functionCallingTransactionTemplate() {
        return new FunctionCallingTransactionTemplate();
    }

    class FunctionCallingTransactionTemplate extends TransactionTemplate {
        @Override public <T> T execute(TransactionCallback<T> action) throws TransactionException {
            final TransactionStatus irrelevantStatus = null;
            return action.doInTransaction(irrelevantStatus);
        }
    }
}
Now in my test I have:
public class SaveAccountAttributesTransactionTest implements FunctionCallingTransactionTemplateTrait {
    private final ArgumentCaptor propertyCaptor = ArgumentCaptor.forClass(Property.class);
    private final PropertyDao propertyDao = mock(PropertyDao.class);
    private final TransactionTemplate transactionTemplate = functionCallingTransactionTemplate();

    private final SaveAccountAttributesTransaction transaction = new SaveAccountAttributesTransaction(propertyDao, transactionTemplate);
    ...
}
And some test:
    @Test
    public void shouldUpdateOneValueAndPersistOther() throws Exception {
        // given
        when(propertyDao.fetchResourceProperties("root", ACCOUNT)).thenReturn(Lists.newArrayList(
                propertyOf("firstProp", "2.21", "root"),
                propertyOf("secondProp", null, null),
                propertyOf("thirdProp", null, null),
                propertyOf("fourthProp", null, null)
        ));
        SaveAccountAttributesEvent event = new SaveAccountAttributesEvent("root", Lists.newArrayList(
                propertyOf("firstProp", "2.22", "root"),
                propertyOf("secondProp", null, null),
                propertyOf("thirdProp", "1.11", "root"),
                propertyOf("fourthProp","default", null)
        ));

        // when
        transaction.execute(event);

        // then
        verify(propertyDao).updatePropertyValue(anyString(), eq("2.22"));
        verify(propertyDao).persist(propertyCaptor.capture());
        assertThat(propertyCaptor.getAllValues()).extracting(Property::getResourceId, Property::getValue)
                .containsOnly(tuple("root", "1.11"));
    }
And it passess :) As you can see I verify behaviour of propertyDao which is being invoked by our extended TransactionTemplate. Hope it helps.