avatarEmanuel Trandafir

Free AI web copilot to create summaries, insights and extended knowledge, download it at here

4566

Abstract

figcaption></figure><h2 id="0702">Testing Strings and Numbers</h2><p id="df00">Similarly, <a href="https://www.java67.com/2014/11/how-to-test-if-array-contains-certain-value-in-java.html">testing Strings</a> will be much easier with AsserJ. I highly encourage you to check it out and read through its API. Here are some helpful methods:</p><div id="6dbf"><pre><span class="hljs-meta">@Test</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">testStrings</span><span class="hljs-params">()</span> { assertThat(<span class="hljs-string">" ignore whitespace "</span>) .isEqualToIgnoringWhitespace(<span class="hljs-string">"ignore whitespace"</span>);

assertThat(<span class="hljs-string">"IGNORE case"</span>) .isEqualToIgnoringCase(<span class="hljs-string">"ignore case"</span>);

assertThat(<span class="hljs-string">""</span>).isBlank();

assertThat(<span class="hljs-string">"<p> this is a test. </p>"</span>) .startsWith(<span class="hljs-string">"<p>"</span>) .endsWith(<span class="hljs-string">"</p>"</span>);

assertThat(<span class="hljs-string">"aGVsbG8gd29ybGQ="</span>) .isBase64(); }</pre></div><p id="1d56">Additionally, AssertJ allows you to write fluent asserts. In other words, we can chain asserts in a nice, declarative way:</p><div id="0198"><pre><span class="hljs-keyword">@Test</span> void testNumbers() { <span class="hljs-built_in">assertThat</span>(<span class="hljs-number">5</span>) <span class="hljs-selector-class">.isOdd</span>() <span class="hljs-selector-class">.isPositive</span>() <span class="hljs-selector-class">.isLessThan</span>(<span class="hljs-number">10</span>); }</pre></div><h2 id="aa98">Testing Exceptions</h2><p id="f22d">AssertJ’s “<i>ThrowableAssert</i>” provide a very nice API for testing the exceptions as well, we can <a href="https://javarevisited.blogspot.com/2013/04/JUnit-tutorial-example-test-exception-thrown-by-java-method.html">check the exception type</a>, the error message, and the stack tarce:</p><div id="184d"><pre><span class="hljs-meta">@Test</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">testIfHasPropertyName</span><span class="hljs-params">()</span> { assertThatThrownBy(() -> testedFunction()) .isInstanceOf(IllegalArgumentException.class) .hasMessageContaining(<span class="hljs-string">"test exception"</span>) .hasStackTraceContaining(<span class="hljs-string">"ro.etrandafir.medium.assertions.AssertionsTest.testedFunction"</span>); }

<span class="hljs-keyword">void</span> <span class="hljs-title function_">testedFunction</span><span class="hljs-params">()</span> { <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">IllegalArgumentException</span>(<span class="hljs-string">"this is a test exception"</span>); }</pre></div><h2 id="c803">Testing Domain Objects</h2><p id="6c58">Finally, let’s learn how to create custom asserts for our domain objects. In my opinion, creating custom asserts is very important if you want to have expressive tests that help you understand the domain logic.</p><p id="3098">For a quick example, let’s assume we have a the following <i>Account</i> model:</p><div id="c85c"><pre><span class="hljs-keyword">record</span> <span class="hljs-title class_">Account</span><span class="hljs-params">(Long id, String name, Status status)</span> {

<span class="hljs-keyword">static</span> Account <span class="hljs-title function_">newAccount</span><span class="hljs-params">(String name)</span> { <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Account</span>(<span class="hljs-literal">null</span>, name, Status.PENDING); }

}</pre></div><p id="99cd">One way to create custom asserts for it would be to extend AssertJ’s <i>AbstractAssert</i>:</p><div id="d6e9"><pre><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">AccountAssert</span> <span class="hljs-keyword">extends</span> <span class="hljs-title class_">AbstractAssert</span><AccountAssert, Account> {

<span class="hljs-keyword">public</span> <span class="hljs-title function_">AccountAssert</span><span class="hljs-params">(Account account)</span> { <span class="hljs-built_in">super</span>(account, AccountAssert.class); }

<span class="hljs-keyword">public</span> <span class="hljs-keyw

Options

ord">static</span> AccountAssert <span class="hljs-title function_">assertThat</span><span class="hljs-params">(Account actual)</span> { <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">AccountAssert</span>(actual); }

<span class="hljs-keyword">public</span> AccountAssert <span class="hljs-title function_">hasName</span><span class="hljs-params">(String expectedName)</span> { <span class="hljs-built_in">super</span>.isNotNull(); Assertions.assertThat(actual.name()) .isEqualTo(expectedName); <span class="hljs-keyword">return</span> <span class="hljs-built_in">this</span>; }

<span class="hljs-keyword">public</span> AccountAssert <span class="hljs-title function_">isNotActive</span><span class="hljs-params">()</span> { <span class="hljs-built_in">super</span>.isNotNull(); Assertions.assertThat(actual.status()) .isNotEqualTo(Account.Status.ACTIVE); <span class="hljs-keyword">return</span> <span class="hljs-built_in">this</span>; }

<span class="hljs-keyword">public</span> AccountAssert <span class="hljs-title function_">hasId</span><span class="hljs-params">(<span class="hljs-type">long</span> expectedId)</span> { <span class="hljs-built_in">super</span>.isNotNull(); Assertions.assertThat(actual.id()) .isEqualTo(expectedId); <span class="hljs-keyword">return</span> <span class="hljs-built_in">this</span>; }

<span class="hljs-keyword">public</span> AccountAssert <span class="hljs-title function_">hasNoId</span><span class="hljs-params">()</span> { <span class="hljs-built_in">super</span>.isNotNull(); Assertions.assertThat(actual.id()).isNull(); <span class="hljs-keyword">return</span> <span class="hljs-built_in">this</span>; } }</pre></div><p id="6c80">As a result, we will be able to test the Account class in a fluent, expressive way:</p><div id="5e50"><pre><span class="hljs-meta">@Test</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">testDomainObjects</span><span class="hljs-params">()</span> { <span class="hljs-type">Account</span> <span class="hljs-variable">canceledAccount</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Account</span>(<span class="hljs-number">1l</span>, <span class="hljs-string">"john doe"</span>, Status.CLOSED); AccountAssert.assertThat(canceledAccount) .hasId(<span class="hljs-number">1l</span>) .hasName(<span class="hljs-string">"john doe"</span>) .isNotActive();

<span class="hljs-type">Account</span> <span class="hljs-variable">newAccount</span> <span class="hljs-operator">=</span> Account.newAccount( <span class="hljs-string">"jane doe"</span>); AccountAssert.assertThat(newAccount) .hasNoId() .hasName(<span class="hljs-string">"jane doe"</span>) .isNotActive(); }</pre></div><p id="0762">Of course, this is a very simple example, but it can potentially be extended to cover many common business checks.</p><h2 id="065b">Conclusion</h2><p id="969d">In this article, we’ve explored various ways of creating unit-test assertions using Java’s AssertJ. We explored its fluent API and learned how to test collections, numbers, Strings, and exceptions.</p><p id="be33">After that, we created our own custom assertion and we saw how to leverage it to build highly expressive tests.</p><figure id="dd4f"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/0*3YgUSwJ4ZQZlXx1W"><figcaption>Photo by <a href="https://unsplash.com/@markusspiske?utm_source=medium&amp;utm_medium=referral">Markus Spiske</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><h1 id="b797">Thank You!</h1><p id="6980">Thanks for reading the article and please let me know what you think! Any feedback is welcome.</p><p id="25ee">If you want to read more about clean code, design, unit testing, functional programming, and many others, make sure to check out <a href="https://readmedium.com/start-here-6d2b065a626">my other articles</a>. Do you like the content? Consider <a href="https://medium.com/@emanueltrandafir">following or subscribing</a> to the email list.</p><p id="4151">Finally, if you consider becoming a Medium member and supporting my blog, here’s my <a href="https://medium.com/@emanueltrandafir/membership">referral</a>.</p><p id="e2a3">Happy Coding!</p></article></body>

Java’s AssertJ: The Secret Weapon for Painless Testing

Writing clean and expressive tests with Java’s AssertJ library. Let’s learn how to test collections, primitives, exceptions, and domain objects!

Photo by Gioele Fazzeri on Unsplash

In this article, we’ll discuss some features of Java’s AssertJ library. We’ll see how easy it is to test various types of data and to understand what went wrong when the tests are failing.

Moreover, we’ll learn how to create our own custom asserts based on the domain models, and use them to improve the readability of our tests.

1. Testing Collections

Let’s assume we want to write an assertion to check that a list contains two exact values. In most cases, I have seen code using the static assertEquals method0 imported from org.junit.jupiter.api.Assertions:

// import static org.junit.jupiter.api.Assertions.assertEquals;

@Test
void test() {
   List<Integer> evenNumbers = List.of(1, 2, 3, 4, 5).stream()
         .filter(nr -> nr % 2 == 0)
         .toList();

   assertEquals(2, evenNumbers.size());
   assertEquals(2, evenNumbers.get(0));
   assertEquals(4, evenNumbers.get(1));
}

This assertion has 2 problems: first, it is hard to understand, and we cannot quickly say what is being tested, especially if the use case is a bit more complex.

Secondly, if this test fails, it will take us a while to figure out what went wrong and how to fix it. The <click here to see differences> link is not very helpful either. (for demonstration purposes, I will add “0” to the initial list, but without updating the assertion)

From this point of view, AssertJ will be a huge upgrade! To create an “Assert” object we can use the factory method assertThat:

org.assertj.core.api.Assertions.assertThat( argument )

Then, based on the type of the argument object, a different type of Assert will be returned. Each of these Assert objects will provide a nice API to test the argument. For instance, if we create it using a list, we’ll have list-related methods:

Therefore, we can replace the previous asserts with containsExactly — this will check the size, the elements, and their order:

@Test
void test2() {
   List<Integer> evenNumbers = List.of(1, 2, 3, 4, 5).stream()
         .filter(nr -> nr % 2 == 0)
         .toList();

   assertThat(evenNumbers)
         .containsExactly(2, 4);
}

Moreover, if add back the “0” element and make the test fail, the message will be way more explicit and easy to understand:

Testing Strings and Numbers

Similarly, testing Strings will be much easier with AsserJ. I highly encourage you to check it out and read through its API. Here are some helpful methods:

@Test
void testStrings() {
   assertThat("  ignore whitespace  ")
         .isEqualToIgnoringWhitespace("ignore whitespace");

   assertThat("IGNORE case")
         .isEqualToIgnoringCase("ignore case");

   assertThat("").isBlank();

   assertThat("<p> this is a  test. </p>")
         .startsWith("<p>")
         .endsWith("</p>");

   assertThat("aGVsbG8gd29ybGQ=")
         .isBase64();
}

Additionally, AssertJ allows you to write fluent asserts. In other words, we can chain asserts in a nice, declarative way:

@Test
void testNumbers() {
   assertThat(5)
         .isOdd()
         .isPositive()
         .isLessThan(10);
}

Testing Exceptions

AssertJ’s “ThrowableAssert” provide a very nice API for testing the exceptions as well, we can check the exception type, the error message, and the stack tarce:

@Test
void testIfHasPropertyName() {
 assertThatThrownBy(() -> testedFunction())
   .isInstanceOf(IllegalArgumentException.class)
   .hasMessageContaining("test exception")
   .hasStackTraceContaining("ro.etrandafir.medium.assertions.AssertionsTest.testedFunction");
}

void testedFunction() {
 throw new IllegalArgumentException("this is a test exception");
}

Testing Domain Objects

Finally, let’s learn how to create custom asserts for our domain objects. In my opinion, creating custom asserts is very important if you want to have expressive tests that help you understand the domain logic.

For a quick example, let’s assume we have a the following Account model:

record Account(Long id, String name, Status status) {
  
   static Account newAccount(String name) {
     return new Account(null, name, Status.PENDING);
   } 

}

One way to create custom asserts for it would be to extend AssertJ’s AbstractAssert:

public class AccountAssert extends AbstractAssert<AccountAssert, Account> {

   public AccountAssert(Account account) {
      super(account, AccountAssert.class);
   }

   public static AccountAssert assertThat(Account actual) {
      return new AccountAssert(actual);
   }

   public AccountAssert hasName(String expectedName) {
      super.isNotNull();
      Assertions.assertThat(actual.name())
            .isEqualTo(expectedName);
      return this;
   }

   public AccountAssert isNotActive() {
      super.isNotNull();
      Assertions.assertThat(actual.status())
            .isNotEqualTo(Account.Status.ACTIVE);
      return this;
   }

   public AccountAssert hasId(long expectedId) {
      super.isNotNull();
      Assertions.assertThat(actual.id())
            .isEqualTo(expectedId);
      return this;
   }

   public AccountAssert hasNoId() {
      super.isNotNull();
      Assertions.assertThat(actual.id()).isNull();
      return this;
   }
}

As a result, we will be able to test the Account class in a fluent, expressive way:

@Test
void testDomainObjects() {
   Account canceledAccount = new Account(1l, "john doe", Status.CLOSED);
   AccountAssert.assertThat(canceledAccount)
         .hasId(1l)
         .hasName("john doe")
         .isNotActive();

   Account newAccount = Account.newAccount( "jane doe");
   AccountAssert.assertThat(newAccount)
         .hasNoId()
         .hasName("jane doe")
         .isNotActive();
}

Of course, this is a very simple example, but it can potentially be extended to cover many common business checks.

Conclusion

In this article, we’ve explored various ways of creating unit-test assertions using Java’s AssertJ. We explored its fluent API and learned how to test collections, numbers, Strings, and exceptions.

After that, we created our own custom assertion and we saw how to leverage it to build highly expressive tests.

Photo by Markus Spiske on Unsplash

Thank You!

Thanks for reading the article and please let me know what you think! Any feedback is welcome.

If you want to read more about clean code, design, unit testing, functional programming, and many others, make sure to check out my other articles. Do you like the content? Consider following or subscribing to the email list.

Finally, if you consider becoming a Medium member and supporting my blog, here’s my referral.

Happy Coding!

Unit Testing
Spring Boot
Java
Software Development
Programming
Recommended from ReadMedium