Java JUnit Tests

From RHS Wiki
Revision as of 13:42, 23 June 2022 by Rafahsolis (talk | contribs) (→‎Configure maven to run tests)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

Add Maven dependencies for JUnit

....
    <dependencies>
        ....
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter</artifactId>
            <version>5.8.2</version>
            <scope>test</scope>
        </dependency>
        ....
    </dependencies>
....

Package structure to create tests

The convention is tu follow the structure shown on the image.

Test example

package com.luv2code.junitdemo;
import org.junit.jupiter.api.Test;
// import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.DisplayNameGeneration;
import org.junit.jupiter.api.DisplayNameGenerator;
import static org.junit.jupiter.api.Assertions.*;

// https://leeturner.me/posts/building-a-camel-case-junit5-displaynamegenerator/
// @DisplayNameGeneration(DisplayNameGenerator.Simple.class)
// @DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class)
@DisplayNameGeneration(DisplayNameGenerator.IndicativeSentences.class)
class DemoUtilsTest {
    DemoUtils demoUtils;

    @BeforeAll
    static void setUpClass() {
        System.out.println("Cleanup before all tests")
    }

    @BeforeEach
    void setUp{
        demoUtils = new DemoUtils();
        int expected = 6;
    }

    @Test
    // @DisplayName("Null and Not Null")
    void testEqualsAndNotEquals() {
       // Execute
       int actual = demoUtils.add(2, 4);

       // Assert
       assertEquals(expected, actual, "2 + 4 must be 6");
    }

    @Test
    void testNullAndNotNull(){
        String str1 = null;
        String str2 = "luv2code";
        
        assertNull(demoUtils.checkNull(str1), "Object should be null");
        assertNull(demoUtils.checkNull(str2), "Object should not be null");
    }

    @AfterEach
    void tearDownAfterEach(){
         System.out.println("Cleanup After each test");
    }

    @AfterAll
    static void cleanUpClass(){
         System.out.println("Cleanup After all tests");
    }
}

Custom displayName generator for camelCase with numbers

static class ReplaceCamelCaseEmojis extends ReplaceCamelCase {
    public ReplaceCamelCaseEmojis() {
    }

    public String generateDisplayNameForClass(Class<?> testClass) {
        return this.replaceWithEmojis(super.generateDisplayNameForClass(testClass));
    }

    public String generateDisplayNameForNestedClass(Class<?> nestedClass) {
        return this.replaceWithEmojis(super.generateDisplayNameForNestedClass(nestedClass));
    }

    public String generateDisplayNameForMethod(Class<?> testClass, Method testMethod) {
        return this.replaceWithEmojis(super.generateDisplayNameForMethod(testClass, testMethod));
    }

    private String replaceWithEmojis(String name) {
        name = name.replaceAll("Camel|camel", "\uD83D\uDC2B");
        name = name.replaceAll("Case|case", "\uD83D\uDCBC");
        name = name.replaceAll("Display|display", "\uD83D\uDCBB");
        name = name.replaceAll("Divisible|divisible", "\u2797");
        name = name.replaceAll("Year|year", "\uD83D\uDCC5");
        name = name.replaceAll("100", "\uD83D\uDCAF");
        return name;
    }
}

Run unit test

All tests

mvn test

Single test

mvn -Dtest=TestMessageBuilder test

Single test method from one test class

mvn -Dtest=TestMessageBuilder#testHelloWorld test

JUnit Assertions


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

org.junit.jupiter.api.Assertions contains JUnit Assertions

assertEquals(expected, actual, optional_message)

assertNotEquals(unexpected, actual, optional_message)

assertNull()

assertNotNull()

assertSame(object1, object1, message)

assertNotSame(object1, object1, message)

assertTrue(boolvariable, message)

assertFalse(boolvariable, message)

assertArraysEqual(array1, array2, message)

assertIterableEquals(iterable1, iterable2 message)

assertLinesMatch??

assertThrows(Exception.class, ()-> {functionCallThatThrowsError(params);})

assertDoesNotThrows( ()-> {functionCallThatDoesNotThrowsError(params);})

assertTimeoutPreemptively(Duration.ofSeconds(3), () -> { demoUtils.checkTimeout(); }, "Method should execute in 3 seconds");

Run tests in order

By default the order of the tests is deterministic but not obious, you can force the order

// MethodOrderer.DisplayName, MethodOrderer..MethodName, MethodOrderer.Random, MethodOrderer.OrderAnnotation

@TestMethodOrder(MethodOrderer.DisplayName.class)
class DemoUtilsTest {
...
    @DisplayName("Equals and not equals")
    void testEqualsAndNotEquals()...

// ----------------------------------------------

@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
class DemoUtilsTest {
...
    @DisplayName("Equals and not equals")
    @Order(1)
    void testEqualsAndNotEquals()...

Configure maven to run tests

pom.xml

    <dependency>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-surefire-plugin</artifactId>
      <version>3.0.0-M5</version>
      <scope>test</scope>
    </dependency>

Code coverage

IntelliJ has built-in support for code coverage Maven can too, requires plugin like maven-surefire-report-plugin

<build>
    <plugins>
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-surefire-report-plugin</artifactId>
      <version>3.0.0-M5</version>
      <executions>
          <execution>
              <phase>test</phase>
              <goals>
                  <goal>report</goal>
              <goals> 
          </execution>
      </executions>      

    </plugin>
</plugins>
</build>

mvn clean test

mvn site -DgenerateReports=false


// at the surefire plugin

....
             <configuration>
                 <testFailureIgnore>true</testFailureIgnore>
                 <statelessTestsetReporter implementation="org.apache.maven.plugin.surefire.extensions.junit5.JUnit5Xml30StatelessReporter">
                     <usePhrasedTestCaseMethodName>true</usePhrasedTestCaseMethodname>
                 </statelessTestsetReporter>
             </configuration>
        </plugin>
.....

JaCoCo (Java Code Coverage)

...
<plugin>
    <groupId>org.jacoco</groupId>
    <artifactId>jacoco-maven-plugin</artifactId>
    <version>0.8.7</version>

    <executions>
        <execution>
            <id>jacoco-prepare</id>
            <goals>
                 <goal>prepare-agent</goal>
            </goals>
        </execution>
        <execution>
             <id>jacoco-report</id>
             <phase>test</phase>
             <goals>
                  <goal>report</goal>
             </goals>
        </execution>
    </executions>
</plugin>
....

Disable tests

class ConditionalTest {
    @Test
    @Disabled("Dont run this test until ticket @34 is solved")
    void basicTest() {}

    @Test
    @EnabledOnOs(OS.WINDOWS, OS.MAC, OS.LINUX)
    void testForAllSystems() {}

    @Test
    @EnabledOnJre(JRE.JAVA_17)
    void testOnJavaVersion() {}

    @Test
    @EnabledOnJreRange(min=JRE.JAVA_13, max=JRE.JAVA_18)  // If only min provided works until latest java
    void testOnJavaVersionRange() {}

    @Test
    @EnabledIfSystemProperty(named="SOMEPROPERTYNAME", matches="SOMEVALUE")
    void testOnJavaVersion() {}

    @Test
    @EnabledIfEnvironmentVariable(named="SOMEPROPERTYNAME", matches="SOMEVALUE")
    void testOnJavaVersion() {}


}

Parameterized Tests

// @parameterizedTest
@parameterizedTest(name="value={0}, expected={1}")
/* @CsvSource({
    "1,1",
    "2,2",
    "3,Fizz",
    "4,4",
    "5,Buzz"
})*/
@CsvFileSource(resources="/small-test-data.csv")  // src/test/resources/small-test-data.csv
void testCsvData(int value, String expected){
    assertEquals(expected, FizzBuzz.compute(value));
}