An In-Depth Guide to JUnit Assertions

JUnit assertions are a critical component of unit testing in Java. They allow you to validate that your code is returning the expected results. In this comprehensive guide, we‘ll cover everything you need to know about using assertions effectively.

What Are JUnit Assertions?

JUnit assertions are methods that verify outcomes in your Java code. You use them to check if an actual result matches what you expect to happen.

For example, you might assert that a calculation returns a specific value:

@Test
public void calculateTotal_basic() {
  int result = Calculator.add(2, 2);

  // Verify the result is what we expect
  assertEquals(4, result); 
}

If the actual result matches the expected value, the assertion passes. If they differ, the assertion fails – indicating a potential bug.

JUnit groups assertions into Assertion classes:

  • JUnit 4org.junit.Assert
  • JUnit 5org.junit.jupiter.api.Assertions

So when you see assert* methods, like assertEquals() – they come from these Assertion classes.

Why Use Assertions in Unit Tests?

Assertions give you confidence that your code behaves how you expect it to. Without them, you might have passing tests that don‘t actually validate anything!

Some key benefits of using assertions include:

Pinpoint Bugs – When an assertion fails, you know exactly which check failed and under what conditions. This makes tracking down bugs much faster.

Improve Design – If your tests need complicated assertions, it often indicates your code needs refactoring to simplify. Assertions encourage better separation of concerns.

Document Functionality – The checks you assert for serve as documentation for what your code should do. Assertions clarify the code‘s intent.

Refactor Safely – Existing assertions give you safety nets when refactoring. You‘ll quickly find out if changes break expected behavior.

By validating expectations, assertions build confidence that your code works as intended. They are invaluable for writing effective unit tests.

Types of Assert Methods

JUnit includes numerous assert methods for validating expected outcomes. Some common examples include:

assertEquals() – Checks test input against an expected value. Variants check numbers, strings, etc.

assertTrue()/assertFalse() – Ensures booleans meet specified conditions.

assertNull()/assertNotNull() – Validates if an object is null or not.

assertSame()/assertNotSame() – Checks if two variables refer to the same object.

assertThrows() – Verifies that code throws a specific exception.

The full JUnit Assertion classes contain over 50 assert methods! There‘s a specialized assertion for practically every use case.

We‘ll cover some of the most popular methods with code examples next.

Using JUnit 4 Assertions

Let‘s demonstrate some JUnit 4 assertions with an example test case:

import static org.junit.Assert.*;

public class MathUtilsTest {

  @Test
  public void calculateAbsoluteValue_positiveNumber() {

    int actual = MathUtils.calculateAbsoluteValue(5);

    // Assertion succeeds:
    assertEquals(5, actual);       
  }

  @Test 
  public void calculateAbsoluteValue_negativeNumber() {

    int actual = MathUtils.calculateAbsoluteValue(-10);

    // Assertion succeeds:
    assertEquals(10, actual);              
  }

}

This test class checks the behavior of a calculateAbsoluteValue() method. Key points:

  • Import static org.junit.Assert.* to have access to all assert methods from the JUnit Assert class.
  • Use assertEquals() to verify the absolute value calculation returns expected results for positive and negative inputs.
  • If any assertions fail, the test method stops and is marked as failed.

Let‘s explore some more JUnit 4 assertion options…

assertTrue() & assertFalse()

Check that boolean conditions are true or false:

  @Test
  public void isAdult_under18() {

    boolean actual = MathUtils.isAdult(17); 

    assertFalse(actual);    
  }

assertNull() & assertNotNull()

Validate if an object is null or not:

  @Test
  public void getMaxNumber_emptyArray() {

    int[] numbers = {};
    Integer actual = MathUtils.getMax(numbers);

    assertNull(actual);
  } 

assertSame() & assertNotSame()

Check if two object variables refer to the same object:

  @Test
  public void calculateAbsoluteValue_noSideEffects() {

    int input = -5;
    int actual = MathUtils.calculateAbsoluteValue(input);

    // input should remain unchanged 
    assertSame(input, -5);              
  }

assertArrayEquals()

Compare expected and actual arrays:

  @Test
  public void getEvenNumbers() {

    int[] actual = MathUtils.getEvenNumbers(1, 10); 
    int[] expected = {2, 4, 6, 8, 10};

    assertArrayEquals(expected, actual);  
  }

And over 40 more comparison and validation methods – check the JUnit 4 documentation!

Now let‘s look at how assertions work in JUnit 5.

Using JUnit 5 Assertions

JUnit 5 is the latest major version of the framework. Here‘s an example test class using some JUnit 5 assertions:

import static org.junit.jupiter.api.Assertions.*;

public class StringUtilsTest {

  @Test
  public void concatStrings_basic() {

    String result = StringUtils.concat("Hello", "World");  

    assertEquals("HelloWorld", result);    
  }

  @Test
  public void stringLength_basic() {

    int length = StringUtils.getLength("Hello");

    assertTrue(length == 5);
  }

  @Test
  public void substring_invalidIndex() {

    assertThrows(IndexOutOfBoundsException.class, () -> {
      StringUtils.getSubstring("Hello", 10); 
    });   
  }

}

Some things to note:

  • Import JUnit 5‘s org.junit.jupiter.api.Assertions class to get access to assert methods.
  • Use assertEquals(), assertTrue(), assertThrows() and more to validate expectations.
  • Tests fail at the first failing assertion.
  • Tests now use lambda syntax to call code under test (() -> { ... }).

Let‘s cover a few key methods available in JUnit 5 assertions…

Dynamic Messages

JUnit 5 assertions allow you to provide custom failure messages using lambda functions:

  @Test
  public void stringLength_emptyString() {

    int length = StringUtils.getLength("");

    assertEquals(0, length, 
      () -> "Expected length of empty string to be 0"); 
  }

The lambda provides a dynamic message only on test failure.

Grouping Assertions

assertAll() allows grouping multiple assertions – if one fails, the rest still run:

  @Test
  public void concatStrings_various() {

    String output1 = StringUtils.concat("Hello", null);
    String output2 = StringUtils.concat("", "World"); 

    assertAll("concat",
      () -> assertEquals("Hello", output1),
      () -> assertEquals("World", output2)  
    );
  }

Helps validate multiple aspects in one test case.

Dependent Assertions

assertTrue() and assertFalse() support passing BooleanSupplier functions:

  @Test
  public void stringLength_notEmpty() {

    BooleanSupplier testLogic = () ->  
      StringUtils.getLength("Hello") > 0;

    assertTrue(testLogic);
  }

This couples the assertion to test evaluation logic.

And Lots More…

Further JUnit 5 assertions include:

  • assertThrows() – Assert an exception is thrown
  • assertTimeout() – Validate test completes in allotted time
  • assertArrayEquals() – Check array equality
  • assertLinesMatch() – Compare string lines

And many more helpful checks – see JUnit 5‘s Assertions documentation.

Now let‘s clarify the difference between two common assertion methods…

assertEquals vs assertSame

A common confusion that arises among testers new to assertions is: what‘s the difference between assertEquals() and assertSame()?

At first glance, they seem to do identical equality checks. But the semantics differ:

  • assertEquals() checks that two primitive/object values are equal.
  • assertSame() checks that two object references point to the exact same object.

Take this example:

@Test 
public void equalityCheck() {

  String value1 = new String("demo"); 
  String value2 = new String("demo");

  // Succeeds - values are logically equal
  assertEquals(value1, value2); 

  // Fails - references refer to different objects
  assertSame(value1, value2);
}

Even though value1 and value2 contain the same string content, they are distinct String objects.

So assertEquals() compares the string values, and sees they are equal. But assertSame() compares object references, which differ.

Make sense? Keep this semantic difference in mind when choosing assertions.

Best Practices Using Assertions

Here are some key best practices to apply as you write assertion checks:

  • Have a single assertion per test case
  • Validate all key expectations, not just one happy path
  • Use specific assertion types, don‘t just check true/false booleans
  • Assert expected functionality, not implementations
  • Provide good assertion failure messages
  • Use utility methods to simplify assertions

Following these tips will ensure your assertions validate all important aspects of your code‘s behavior.

Conclusion

This guide covered the key concepts for getting started using assertions for unit testing in Java and JUnit:

  • What assertions are – Validation methods that verify code behaviors
  • Why assertions matter – Improve defect detection, clarify expected functionality
  • Types of assert methods – Over 50 different checks to choose from
  • JUnit 4 vs JUnit 5 – Modern assertions with dynamic failure messages
  • assertSame() vs assertEquals() – Compare same objects vs equal values
  • Best practices – Focus assertions on expected functionality

Assertions form a critical part of tests – guiding development to create robust, high-quality code.

Put these JUnit assertion techniques into practice in your test classes. You will find they simplify validating the growing complexity of application logic over time.

Happy asserting your Java code!

Read More Topics