| Line 113: |
Line 113: |
| | ==Unit tests== | | ==Unit tests== |
| | | | |
| − | ===Add Maven dependencies for JUnit===
| |
| − | <syntaxhighlight lang="xml">
| |
| − | ....
| |
| − | <dependencies>
| |
| − | ....
| |
| − | <dependency>
| |
| − | <groupId>org.junit.jupiter</groupId>
| |
| − | <artifactId>junit-jupiter</artifactId>
| |
| − | <version>5.8.2</version>
| |
| − | <scope>test</scope>
| |
| − | </dependency>
| |
| − | ....
| |
| − | </dependencies>
| |
| − | ....
| |
| − | </syntaxhighlight>
| |
| − |
| |
| − | ===Package structure to create tests===
| |
| − | [[File:Pstructjstest.png|thumb|alt=|none]]
| |
| − |
| |
| − | The convention is tu follow the structure shown on the image.
| |
| − |
| |
| − | ===Test example===
| |
| − | <syntaxhighlight lang="java">
| |
| − | 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");
| |
| − | }
| |
| − | }
| |
| − | </syntaxhighlight>
| |
| − |
| |
| − | ====Custom displayName generator for camelCase with numbers====
| |
| − | <syntaxhighlight lang="java">
| |
| − | 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;
| |
| − | }
| |
| − | }
| |
| − | </syntaxhighlight>
| |
| − |
| |
| − | ===Run unit test===
| |
| − |
| |
| − | ====All tests====
| |
| − | <syntaxhighlight lang="bash">
| |
| − | mvn test
| |
| − | </syntaxhighlight>
| |
| − |
| |
| − | ====Single test====
| |
| − | <syntaxhighlight lang="bash">
| |
| − | mvn -Dtest=TestMessageBuilder test
| |
| − | </syntaxhighlight>
| |
| − |
| |
| − | ====Single test method from one test class====
| |
| − | <syntaxhighlight lang="bash">
| |
| − | mvn -Dtest=TestMessageBuilder#testHelloWorld test
| |
| − | </syntaxhighlight>
| |
| − |
| |
| − | ===JUnit Assertions===
| |
| − | <br /><syntaxhighlight lang="java">
| |
| − | import static org.junit.jupiter.api.Assertions.*;
| |
| − | </syntaxhighlight>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<syntaxhighlight lang="java">
| |
| − | // 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()...
| |
| − | </syntaxhighlight>
| |
| − |
| |
| − | === Configure maven to run tests ===
| |
| − | pom.xml<syntaxhighlight lang="xml">
| |
| − | <!-- Spring Boot -->
| |
| − |
| |
| − | <dependency>
| |
| − | <groupId>org.springframework.boot</groupId>
| |
| − | <artifactId>spring-boot-starter-test</artifactId>
| |
| − | <scope>test</scope>
| |
| − | </dependency>
| |
| − |
| |
| − | <!-- Java -->
| |
| − | <dependency>
| |
| − | <groupId>org.apache.maven.plugins</groupId>
| |
| − | <artifactId>maven-surefire-plugin</artifactId>
| |
| − | <version>3.0.0-M5</version>
| |
| − | <scope>test</scope>
| |
| − | </dependency>
| |
| − |
| |
| − | </syntaxhighlight>
| |
| − |
| |
| − | === Code coverage ===
| |
| − | IntelliJ has built-in support for code coverage Maven can too, requires plugin like maven-surefire-report-plugin<syntaxhighlight lang="xml">
| |
| − | <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>
| |
| − | </syntaxhighlight>mvn clean test
| |
| − |
| |
| − | mvn site -DgenerateReports=false
| |
| − | <br /><syntaxhighlight lang="xml">
| |
| − | // at the surefire plugin
| |
| − |
| |
| − | ....
| |
| − | <configuration>
| |
| − | <testFailureIgnore>true</testFailureIgnore>
| |
| − | <statelessTestsetReporter implementation="org.apache.maven.plugin.surefire.extensions.junit5.JUnit5Xml30StatelessReporter">
| |
| − | <usePhrasedTestCaseMethodName>true</usePhrasedTestCaseMethodname>
| |
| − | </statelessTestsetReporter>
| |
| − | </configuration>
| |
| − | </plugin>
| |
| − | .....
| |
| − | </syntaxhighlight>
| |
| − |
| |
| − | === JaCoCo (Java Code Coverage) ===
| |
| − | <syntaxhighlight lang="xml">
| |
| − | ...
| |
| − | <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>
| |
| − | ....
| |
| − | </syntaxhighlight>
| |
| − |
| |
| − | === Disable tests ===
| |
| − | <syntaxhighlight lang="java">
| |
| − | 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() {}
| |
| − |
| |
| − |
| |
| − | }
| |
| − | </syntaxhighlight>
| |
| − |
| |
| − | === Parameterized Tests ===
| |
| − | <syntaxhighlight lang="java">
| |
| − | // @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));
| |
| − | }
| |
| − | </syntaxhighlight>
| |
| | | | |
| | ==Example: Spring Boot with Data JPA and in memory database H2== | | ==Example: Spring Boot with Data JPA and in memory database H2== |