If you’ve ever written a unit test in Java, you’ve probably used JUnit 4 and done something like this:
This is okay and gets the job done. However, I think there are some issues with this approach.
If there’s a bug in your
Company class and the
addEmployee method does not work, you will get the following message:
java.lang.AssertionError: Expected :0 Actual :2
Expected 0? Oops,
assertEquals takes the expected value first and the actual value second. That’s an easy mistake to make. So you fix your test and write it as:
And now you get:
java.lang.AssertionError: Expected :2 Actual :0
That’s better. Now you know that the test expects an integer with a value of 2 but received an integer with a value of 0.
But is that really what your test expects? I would argue that you were expecting a collection with a size of 2, not an integer with a size of 2. Sadly, it is not easy to express this with JUnit 4.
Introducing Fest Assertions (or Fluent Assertions)!
Fest Assertions introduces a new static method,
assertThat that gives you the power to easily express complex assertions in a way that allows the assertion engine to know what you want to verify.
The best way to write the above unit test would become:
If this test fails, you will get the following message:
java.lang.AssertionError: expected size:<2> but was:<0> for <>
This is much better! Now we know that we expected a collection with size 2 but the actual size was zero. We can even see the content of the collection being tested. This technique also addresses the problem of
assertEquals() parameter order.
Here are some more examples on how
assertThat can help you.
Another cleaner error message
// Before assertTrue(company.getEmployees().isEmpty()); // After assertThat(company.getEmployees()).isEmpty());
The assertion message
// Before (yes, that's all it says!) java.lang.AssertionError // After java.lang.AssertionError: expecting empty, but was:<['John']>
Code that reads more like english
// Before assertFalse(company.isHiring()); // After assertThat(company.isHiring()).isFalse();
// Before assertTrue(company instanceof Company); // After assertThat(company).isInstanceOf(Company.class);
The assertion message
// Before java.lang.AssertionError // After java.lang.AssertionError: expected instance of:<com.coveo.Company> but was instance of:<java.lang.Object>
EDIT: Here is another way you can enhance the readability of your tests using Java 8.