JUnit assertions are a cornerstone of Java testing, enabling developers to write tests that verify code behavior. In this article, we’ll explore the various JUnit assertions, their applications in Selenium WebDriver tests, and delve into a custom assertion example with the DateTimeAssert class.
Understanding JUnit Assertions
JUnit offers a suite of assertion methods that validate different conditions in tests. Here’s a brief overview of some key methods:
- assertEquals(): Verifies that two values are equal.
- assertNotEquals(): Ensures two values are not equal.
- assertTrue(): Checks that a condition is true.
- assertFalse(): Verifies a condition is false.
- assertNull(): Checks that an object is null.
- assertNotNull(): Ensures an object is not null.
- assertSame(): Tests if two references point to the same object.
- assertNotSame(): Ensures two references do not point to the same object.
- assertArrayEquals(): Verifies that two arrays are equal.
- assertIterableEquals(): Asserts that two iterables are equal.
- assertAll(): Groups multiple assertions in a single test.
- assertThrows(): Expects a specific exception to be thrown.
Here is sample usage of the assertions:
@TestMethodOrder(value = MethodOrderer.Random.class)
public class CalculatorTests {
private final Calculator _calculator = new Calculator();
@BeforeAll
public static void setUpClass() {
System.out.println("This is @BeforeAll annotation");
}
@BeforeEach
public void setUp() {
System.out.println("This is @BeforeEach annotation");
}
@NightlyRunTest
@Order(2)
public void test_Addition() {
System.out.println("This is test 1");
var actualResult = _calculator.add(1, 1);
Assertions.assertEquals(2, actualResult);
}
@Test
@Order(1)
public void testAdditionDifferentNumbers() {
System.out.println("This is test 2");
var actualResult = _calculator.add(2, 1);
Assertions.assertEquals(3, actualResult);
}
@AfterEach
public void tearDown() {
System.out.println("This is @After annotation");
}
@AfterAll
public static void tearDownClass() {
System.out.println("This is @AfterAll annotation");
}
}
The tests are verifying the following simple class:
public class Calculator {
public int add(int a, int b) {
return a + b;
}
public int divide(int a, int b) {
if (b == 0)
throw new ArithmeticException("/ by zero");
return a/b;
}
}
In this snippet, we see the setup for a test class for a Calculator. The @TestMethodOrder(MethodOrderer.Random.class) annotation suggests that the test methods within this class will be executed in a random order. This approach is useful to ensure that tests are independent of each other. The goal here is to test various operations of a Calculator class, such as addition, subtraction, multiplication, and division, ensuring they perform correctly under different scenarios.
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Tag("nightlyRun")
@Test
public @interface NightlyRunTest {
}
The @NightlyRunTest annotation is a custom annotation that combines the standard @Test annotation with a @Tag. This allows for easily filtering and running a specific subset of tests, in this case, those intended for a nightly run.
JUnit Assertions in WebDriver Tests
In Selenium WebDriver tests, assertions are used to validate the state of web elements and interactions. For instance, after clicking a button on a webpage, assertTrue() could be used to assert that a confirmation message is displayed.
public class FirstSeleniumTests {
private WebDriver driver;
@BeforeAll
public static void setUpClass() {
WebDriverManager.chromedriver().setup();
}
@BeforeEach
public void setUp() {
driver = new ChromeDriver();
}
@Test
public void properCheckboxSelected() throws Exception {
driver.navigate().to("https://lambdatest.github.io/sample-todo-app/");
LocalDate birthDay = LocalDate.of(1990, 10, 20);
// us 10/20/1990
DateTimeFormatter usDateFormat = DateTimeFormatter.ofPattern("dd-MM-yyyy");
String dateToType = usDateFormat.format(birthDay);
WebElement todoInput = driver.findElement(By.id("sampletodotext"));
todoInput.sendKeys(dateToType);
var addButton = driver.findElement(By.id("addbutton"));
addButton.click();
var todoCheckboxes = driver.findElements(By.xpath("//li[@ng-repeat]/input"));
todoCheckboxes.get(2).click();
var todoInfos = driver.findElements(By.xpath("//li[@ng-repeat]/span"));
Assertions.assertEquals("20-10-1990", todoInfos.get(5).getText());
String expectedUrl = "https://lambdatest.github.io/sample-todo-app/";
Assertions.assertTrue(expectedUrl.equals(driver.getCurrentUrl()), "URL does not match");
String notExpectedUrl = "https://www.lambdatest.com/";
Assertions.assertFalse(notExpectedUrl.equals(driver.getCurrentUrl()), "URL match");
var expectedItems = new String[] {
"First Item",
"Second Item",
"Third Item",
"Fourth Item",
"Fifth Item",
"20-10-1990"
};
var actualToDoInfos = todoInfos.stream().map(e - > e.getText()).toArray();
Assertions.assertArrayEquals(expectedItems, actualToDoInfos);
Exception exception = Assertions.assertThrows(ArithmeticException.class, () - > new Calculator().divide(1, 0));
Assertions.assertEquals("/ by zero", exception.getMessage());
Assertions.assertTimeout(ofMinutes(2), () - > {
// perform your tasks
});
Assertions.assertAll(
() - > Assertions.assertTrue(expectedUrl.equals(driver.getCurrentUrl()), "URL does not match"),
() - > Assertions.assertFalse(notExpectedUrl.equals(driver.getCurrentUrl()), "URL match"),
() - > Assertions.assertArrayEquals(expectedItems, actualToDoInfos)
);
double actualDoubleValue = 2.999;
double expectedDoubleValue = 3.000;
Assertions.assertEquals(expectedDoubleValue, actualDoubleValue, 0.001);
var currentTime = LocalDateTime.now();
var currentTimeInPast = LocalDateTime.now().minusMinutes(3);
DateTimeAssert.assertEquals(currentTime, currentTimeInPast, DateTimeDeltaType.MINUTES, 4);
}
@AfterEach
public void tearDown() {
if (driver != null) {
driver.quit();
}
}
}
Let’s discuss in details the important parts of the example.
Assertions.assertEquals("20-10-1990", todoInfos.get(5).getText());
This assertion checks if the text of the sixth element in todoInfos matches the string “20-10-1990”. It verifies that the text in the web application is as expected.
Assertions.assertTrue(expectedUrl.equals(driver.getCurrentUrl()), "URL does not match");
This asserts that the current URL of the WebDriver matches the expected URL. The test will pass if the condition is true.
Assertions.assertFalse(notExpectedUrl.equals(driver.getCurrentUrl()), "URL match");
This asserts that the current URL of the WebDriver does not match notExpectedUrl. The test will pass if the URL is different.
Assertions.assertArrayEquals(expectedItems, actualToDoInfos);
This checks if the array of expected items matches the array of texts from todoInfos. It’s used to verify multiple elements in a collection.
Exception exception = Assertions.assertThrows(ArithmeticException.class, () - > new Calculator().divide(1, 0));
Assertions.assertEquals("/ by zero", exception.getMessage());
This tests if the specified action divide(1, 0) throws an ArithmeticException. It then checks if the exception message is as expected.
Deep Dive into DateTimeAssert Class
The DateTimeAssert class illustrates a custom assertion approach.
public class DateTimeAssert {
public static void assertEquals(LocalDateTime expectedDate, LocalDateTime actualDate, DateTimeDeltaType deltaType, int count) throws Exception {
if (((expectedDate == null) && (actualDate == null))) {
return;
}
else if ((expectedDate == null)) {
throw new NullPointerException("The expected date was null");
}
else if ((actualDate == null)) {
throw new NullPointerException("The actual date was null");
}
Duration expectedDelta = DateTimeAssert.getTimeSpanDeltaByType(deltaType, count);
double totalSecondsDifference = Math.abs((actualDate.until(expectedDate, ChronoUnit.SECONDS)));
if ((totalSecondsDifference > expectedDelta.getSeconds())) {
var exceptionMessage =String.format("Expected Date: {0}, Actual Date: {1} \nExpected Delta: {2}, Actual Delta in seconds- {3} (Delta Type: " +
"{4})", expectedDate, actualDate, expectedDelta, totalSecondsDifference, deltaType);
throw new Exception(exceptionMessage);
}
}
private static Duration getTimeSpanDeltaByType(DateTimeDeltaType type, int count) {
Duration result;
switch (type) {
case DAYS:
result = Duration.ofDays(count);
break;
case MINUTES:
result = Duration.ofMinutes(count);
break;
default:
throw new NotImplementedException("The delta type is not implemented.");
}
return result;
}
}
The class provides a method assertEquals(LocalDateTime expectedDate, LocalDateTime actualDate, DateTimeDeltaType deltaType, int count) which compares two LocalDateTime objects with a specified tolerance. This method checks if two dates are within a certain tolerance. The parameters deltaType and count define the type and magnitude of the permissible delta, making this a versatile tool for date comparisons in tests.
To learn even more about the topic check this video:
Conclusion
JUnit assertions are an integral part of Java testing, offering a wide range of methods to validate different conditions. Their application in Selenium WebDriver tests enhances the robustness of web application testing. Custom assertions like those in the DateTimeAssert class further expand the capabilities of JUnit, allowing for more tailored and complex test scenarios.
