2.4. Assertions

JUnit Jupiter comes with many of the assertion methods that JUnit 4 has and adds a few that lend themselves well to being used with Java 8 lambdas. All JUnit Jupiter assertions are static methods in the [org.junit.jupiter.api.Assertions](https://junit.org/junit5/docs/current/api/org.junit.jupiter.api/org/junit/jupiter/api/Assertions.html) class.

  1. import static java.time.Duration.ofMillis;
  2. import static java.time.Duration.ofMinutes;
  3. import static org.junit.jupiter.api.Assertions.assertAll;
  4. import static org.junit.jupiter.api.Assertions.assertEquals;
  5. import static org.junit.jupiter.api.Assertions.assertNotNull;
  6. import static org.junit.jupiter.api.Assertions.assertThrows;
  7. import static org.junit.jupiter.api.Assertions.assertTimeout;
  8. import static org.junit.jupiter.api.Assertions.assertTimeoutPreemptively;
  9. import static org.junit.jupiter.api.Assertions.assertTrue;
  10. import java.util.concurrent.CountDownLatch;
  11. import example.domain.Person;
  12. import example.util.Calculator;
  13. import org.junit.jupiter.api.Test;
  14. class AssertionsDemo {
  15. private final Calculator calculator = new Calculator();
  16. private final Person person = new Person("Jane", "Doe");
  17. @Test
  18. void standardAssertions() {
  19. assertEquals(2, calculator.add(1, 1));
  20. assertEquals(4, calculator.multiply(2, 2),
  21. "The optional failure message is now the last parameter");
  22. assertTrue('a' < 'b', () -> "Assertion messages can be lazily evaluated -- "
  23. + "to avoid constructing complex messages unnecessarily.");
  24. }
  25. @Test
  26. void groupedAssertions() {
  27. // In a grouped assertion all assertions are executed, and all
  28. // failures will be reported together.
  29. assertAll("person",
  30. () -> assertEquals("Jane", person.getFirstName()),
  31. () -> assertEquals("Doe", person.getLastName())
  32. );
  33. }
  34. @Test
  35. void dependentAssertions() {
  36. // Within a code block, if an assertion fails the
  37. // subsequent code in the same block will be skipped.
  38. assertAll("properties",
  39. () -> {
  40. String firstName = person.getFirstName();
  41. assertNotNull(firstName);
  42. // Executed only if the previous assertion is valid.
  43. assertAll("first name",
  44. () -> assertTrue(firstName.startsWith("J")),
  45. () -> assertTrue(firstName.endsWith("e"))
  46. );
  47. },
  48. () -> {
  49. // Grouped assertion, so processed independently
  50. // of results of first name assertions.
  51. String lastName = person.getLastName();
  52. assertNotNull(lastName);
  53. // Executed only if the previous assertion is valid.
  54. assertAll("last name",
  55. () -> assertTrue(lastName.startsWith("D")),
  56. () -> assertTrue(lastName.endsWith("e"))
  57. );
  58. }
  59. );
  60. }
  61. @Test
  62. void exceptionTesting() {
  63. Exception exception = assertThrows(ArithmeticException.class, () ->
  64. calculator.divide(1, 0));
  65. assertEquals("/ by zero", exception.getMessage());
  66. }
  67. @Test
  68. void timeoutNotExceeded() {
  69. // The following assertion succeeds.
  70. assertTimeout(ofMinutes(2), () -> {
  71. // Perform task that takes less than 2 minutes.
  72. });
  73. }
  74. @Test
  75. void timeoutNotExceededWithResult() {
  76. // The following assertion succeeds, and returns the supplied object.
  77. String actualResult = assertTimeout(ofMinutes(2), () -> {
  78. return "a result";
  79. });
  80. assertEquals("a result", actualResult);
  81. }
  82. @Test
  83. void timeoutNotExceededWithMethod() {
  84. // The following assertion invokes a method reference and returns an object.
  85. String actualGreeting = assertTimeout(ofMinutes(2), AssertionsDemo::greeting);
  86. assertEquals("Hello, World!", actualGreeting);
  87. }
  88. @Test
  89. void timeoutExceeded() {
  90. // The following assertion fails with an error message similar to:
  91. // execution exceeded timeout of 10 ms by 91 ms
  92. assertTimeout(ofMillis(10), () -> {
  93. // Simulate task that takes more than 10 ms.
  94. Thread.sleep(100);
  95. });
  96. }
  97. @Test
  98. void timeoutExceededWithPreemptiveTermination() {
  99. // The following assertion fails with an error message similar to:
  100. // execution timed out after 10 ms
  101. assertTimeoutPreemptively(ofMillis(10), () -> {
  102. // Simulate task that takes more than 10 ms.
  103. new CountDownLatch(1).await();
  104. });
  105. }
  106. private static String greeting() {
  107. return "Hello, World!";
  108. }
  109. }
Preemptive Timeouts with assertTimeoutPreemptively()

Contrary to declarative timeouts, the various assertTimeoutPreemptively() methods in the Assertions class execute the provided executable or supplier in a different thread than that of the calling code. This behavior can lead to undesirable side effects if the code that is executed within the executable or supplier relies on java.lang.ThreadLocal storage.

One common example of this is the transactional testing support in the Spring Framework. Specifically, Spring’s testing support binds transaction state to the current thread (via a ThreadLocal) before a test method is invoked. Consequently, if an executable or supplier provided to assertTimeoutPreemptively() invokes Spring-managed components that participate in transactions, any actions taken by those components will not be rolled back with the test-managed transaction. On the contrary, such actions will be committed to the persistent store (e.g., relational database) even though the test-managed transaction is rolled back.

Similar side effects may be encountered with other frameworks that rely on ThreadLocal storage.

2.4.1. Kotlin Assertion Support

JUnit Jupiter also comes with a few assertion methods that lend themselves well to being used in Kotlin. All JUnit Jupiter Kotlin assertions are top-level functions in the org.junit.jupiter.api package.

  1. import example.domain.Person
  2. import example.util.Calculator
  3. import java.time.Duration
  4. import org.junit.jupiter.api.Assertions.assertEquals
  5. import org.junit.jupiter.api.Assertions.assertTrue
  6. import org.junit.jupiter.api.Test
  7. import org.junit.jupiter.api.assertAll
  8. import org.junit.jupiter.api.assertDoesNotThrow
  9. import org.junit.jupiter.api.assertThrows
  10. import org.junit.jupiter.api.assertTimeout
  11. import org.junit.jupiter.api.assertTimeoutPreemptively
  12. class KotlinAssertionsDemo {
  13. private val person = Person("Jane", "Doe")
  14. private val people = setOf(person, Person("John", "Doe"))
  15. @Test
  16. fun `exception absence testing`() {
  17. val calculator = Calculator()
  18. val result = assertDoesNotThrow("Should not throw an exception") {
  19. calculator.divide(0, 1)
  20. }
  21. assertEquals(0, result)
  22. }
  23. @Test
  24. fun `expected exception testing`() {
  25. val calculator = Calculator()
  26. val exception = assertThrows<ArithmeticException> ("Should throw an exception") {
  27. calculator.divide(1, 0)
  28. }
  29. assertEquals("/ by zero", exception.message)
  30. }
  31. @Test
  32. fun `grouped assertions`() {
  33. assertAll("Person properties",
  34. { assertEquals("Jane", person.firstName) },
  35. { assertEquals("Doe", person.lastName) }
  36. )
  37. }
  38. @Test
  39. fun `grouped assertions from a stream`() {
  40. assertAll("People with first name starting with J",
  41. people
  42. .stream()
  43. .map {
  44. // This mapping returns Stream<() -> Unit>
  45. { assertTrue(it.firstName.startsWith("J")) }
  46. }
  47. )
  48. }
  49. @Test
  50. fun `grouped assertions from a collection`() {
  51. assertAll("People with last name of Doe",
  52. people.map { { assertEquals("Doe", it.lastName) } }
  53. )
  54. }
  55. @Test
  56. fun `timeout not exceeded testing`() {
  57. val fibonacciCalculator = FibonacciCalculator()
  58. val result = assertTimeout(Duration.ofMillis(1000)) {
  59. fibonacciCalculator.fib(14)
  60. }
  61. assertEquals(377, result)
  62. }
  63. @Test
  64. fun `timeout exceeded with preemptive termination`() {
  65. // The following assertion fails with an error message similar to:
  66. // execution timed out after 10 ms
  67. assertTimeoutPreemptively(Duration.ofMillis(10)) {
  68. // Simulate task that takes more than 10 ms.
  69. Thread.sleep(100)
  70. }
  71. }
  72. }

2.4.2. Third-party Assertion Libraries

Even though the assertion facilities provided by JUnit Jupiter are sufficient for many testing scenarios, there are times when more power and additional functionality such as matchers are desired or required. In such cases, the JUnit team recommends the use of third-party assertion libraries such as AssertJ, Hamcrest, Truth, etc. Developers are therefore free to use the assertion library of their choice.

For example, the combination of matchers and a fluent API can be used to make assertions more descriptive and readable. However, JUnit Jupiter’s [org.junit.jupiter.api.Assertions](https://junit.org/junit5/docs/current/api/org.junit.jupiter.api/org/junit/jupiter/api/Assertions.html) class does not provide an assertThat() method like the one found in JUnit 4’s org.junit.Assert class which accepts a Hamcrest Matcher. Instead, developers are encouraged to use the built-in support for matchers provided by third-party assertion libraries.

The following example demonstrates how to use the assertThat() support from Hamcrest in a JUnit Jupiter test. As long as the Hamcrest library has been added to the classpath, you can statically import methods such as assertThat(), is(), and equalTo() and then use them in tests like in the assertWithHamcrestMatcher() method below.

  1. import static org.hamcrest.CoreMatchers.equalTo;
  2. import static org.hamcrest.CoreMatchers.is;
  3. import static org.hamcrest.MatcherAssert.assertThat;
  4. import example.util.Calculator;
  5. import org.junit.jupiter.api.Test;
  6. class HamcrestAssertionsDemo {
  7. private final Calculator calculator = new Calculator();
  8. @Test
  9. void assertWithHamcrestMatcher() {
  10. assertThat(calculator.subtract(4, 1), is(equalTo(3)));
  11. }
  12. }

Naturally, legacy tests based on the JUnit 4 programming model can continue using org.junit.Assert#assertThat.