Diary of JUnit Learning

Diary of JUnit Learning

Author:Roser Han

Location:NUAA

Before Reading

You can get the source code from my github repository:

https://github.com/LitMonkey/Java-JUnit-Study-Diary

And welcome to visit my blog:

http://www.litmonkey.cn:81/

Writing Tests

Test Classes and Methods

Test Class: any top-level class, static member class, or @Nested class that contains at least one test method.

Test classes must not be abstract and must have a single constructor.

Test Method: any instance method that is directly annotated or meta-annotated with @Test, @RepeatedTest, @ParameterizedTest, @TestFactory, or @TestTemplate.

Lifecycle Method: any method that is directly annotated or meta-annotated with @BeforeAll, @AfterAll, @BeforeEach, or @AfterEach.

Test methods and life cycle methods may be declared locally within the current class, inherited from superclasses, or inherited from interface.

In addition, test methods and lifecycle methods must not be abstract and must not return a value.

Here is a example of the usage of Test Method and Lifecycle Method:

import static org.junit.jupiter.api.Assertions.fail;
import static org.junit.jupiter.api.Assumptions.assumeTrue;

import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;

public class StandardTests {

    @BeforeAll
    static void initAll(){

    }

    @BeforeEach
    void init(){

    }

    @Test
    void succeedingTest(){

    }

    @Test
    void failingTest(){
        fail("a failing test");
    }

    @Test
    @Disabled("for demonstration purposes")
    void skippedTest(){

    }

    @Test
    void abortedTest(){
        assumeTrue("abc".contains("Z"));
        fail("test should have been aborted");
    }

    @AfterEach
    void tearDown(){

    }
    @AfterAll
    static void tearDownAll(){
        
    }
}

Display Names

Test classes and test methods can declare custom display names via @DisplayName----with spaces, specila characters, and even emojis----that will displayed in test reports and by test runner and IDEs.

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

@DisplayName("A special test case")
public class DisplayNameDemo {
    @Test
    @DisplayName("Custom test name containint spaces")
    void testWithDisplayNameContainingSpaces(){
        
    }
    
    @Test
    @DisplayName("ヾ(≧▽≦*)o")
    void testWithDisplayNameContainingSpecialCharacters(){
        
    }
    @Test
    @DisplayName("😂")
    void testWithDisplayNameContainingEmoji(){
        
    }
}

The name will display as the following image:

在这里插入图片描述

The display name for a test class or method is determined according to the following precedence rules:

  1. value of the @DisplayName annotation, if present
  2. by calling the DisplayNameGenerator specified in the @DisplayNameGeneration annotation, if present
  3. by calling the default DisplayNameGenrator configured via the configuration parameter, if present
  4. by calling org.junit.jupiter.api.DisplayNameGenerator.Standard

Assertions

All JUnit Jupiter assertions are static methods in the org.junit.jupiter.api.Assertions class.

Let’s see the details of assertions.

Frequently-used Method Summary

AssertionDescription
fail(String message)Fail the test with the given failure message
assertTrue(boolean condition)Assert that the supplied condition is true
assertFalse(boolean condition)Assert that the supplied condition is false
assertNull(Object actual, [String message])Assert that actual is null
Fails with the supplied failure message
assertNotNull(Object actual, [String message])Assert that actual is not null
Fails with the supplied failure message
assertEquals(expected, actual)This assertion is the most frequently-used one.
Assert that expected and actual are equal
assertArrayEquals(TypeName[] expeted, TypeName[] actual)Assert that expected and actual array are equal(content)
assertNotEquals(expect, actual)Assert that expected and actual are not equal
assertSame(Object expected, Object actual)Assert that expected and actual refer to the same object
assertNotSame(Object expected, Object actual)Assert that expected and actual refer to the different object
assertAll(excutables) throws MutipleFailuresErrorAssert that all supplied executables do not throw exceptions
assertThrows(exceptedType, excutable)Assert that excution of the supplied excutable throws an exception of the expectedTyle and return the exception
Third-party Assertion Libraries

Developers are 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.

But before using third-party assertion libraries, let’s learn more about the protogenetic JUnit at first.

Assumptions

Assumptions is a collection of utility methods that support conditional test execution based on assumptions.

Assumptions are typically used whenever it does not make sense to continue execution of a given test method — for example, if the test depends on something that does not exist in the current runtime environment.

AssumptionDescription
assumeTrue(assumption)Validate the given assumption
assumeFalse(assumption)Validate the given assumption
assumingThat(assumption, executable)Excute the supplied Excutable, but only if the supplied assumption is valid.
if the assumption is invalid, this method does nothing

Disabling Tests

@Disabled is used to signal that the annotated test class or test method is currently disabled and should not be executed.

Disabled may optionally be declared with a reason to document why the annotated test class ro test method is disabled.

When applied at the class level, all test methods within that class are automatically disabled as well.

Wen applied at the method level, the presence of this annotation does not prevent the test class from being instantiated. Rather, it prevents the execution of the test method and method-level lifecycle callbacks such as @BeforeEach methods, @AfterEach methods, and corresponding extension APIs.

Conditional Test Execution

Script-based Conditions

Here we talk about only one type of Conditional Test Execution----Script-based Conditions.

JUnit Jupiter provides the ability to either enable or disable a container or test depending on the evaluation of a script configured via the @EnabledIf or @DisabledIf annotation.

@Test // Static JavaScript expression.
@EnabledIf("2 * 3 == 6")
void willBeExecuted() {
    // ...
}

@RepeatedTest(10) // Dynamic JavaScript expression.
@DisabledIf("Math.random() < 0.314159")
void mightNotBeExecuted() {
    // ...
}

@Test // Regular expression testing bound system property.
@DisabledIf("/32/.test(systemProperty.get('os.arch'))")
void disabledOn32BitArchitectures() {
    assertFalse(System.getProperty("os.arch").contains("32"));
}

@Test
@EnabledIf("'CI' == systemEnvironment.get('ENV')")
void onlyOnCiServer() {
    assertTrue("CI".equals(System.getenv("ENV")));
}

@Test // Multi-line script, custom engine name and custom reason.
@EnabledIf(value = {
                "load('nashorn:mozilla_compat.js')",
                "importPackage(java.time)",
                "",
                "var today = LocalDate.now()",
                "var tomorrow = today.plusDays(1)",
                "tomorrow.isAfter(today)"
            },
            engine = "nashorn",
            reason = "Self-fulfilling: {result}")
void theDayAfterTomorrow() {
    LocalDate today = LocalDate.now();
    LocalDate tomorrow = today.plusDays(1);
    assertTrue(tomorrow.isAfter(today));
}

Tagging

Test classes and methods can be tagged via the @Tag annotation.

import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;

@Tag("fast")
@Tag("model")
class TaggingDemo {

    @Test
    @Tag("taxes")
    void testingTaxCalculation() {
    }

}

Parameterized Tests

Parameterized tests make it possible to run a test multiple times with different arguments. They are declared just like regular @Test methods but use the @ParaterizedTest annotation instead. In addition, you must declare at least one source that will provide the arguments for each invocation and then consume the arguments in the test method.

The following example demonstrates a parameterized test that uses the @ValueSource annotation to specify a String array as the source of arguments.

@ParameterizedTest
@ValueSource(strings = { "racecar", "radar", "able was I ere I saw elba" })
void palindromes(String candidate) {
    assertTrue(StringUtils.isPalindrome(candidate));
}

To execute the test, there are some changes of the code above:

在这里插入图片描述

Sources of Arguments

JUnit Jupiter provides quite a few source annotations.

@ValueSource

@ValueSource is one of the simplest possible sources. It lets you specify a single array of literal values and can only be used for providing a single argument per parameterized test invocation.

The following types of literal values are supported by @ValueSource.

  • short
  • byte
  • int
  • long
  • float
  • double
  • char
  • boolean
  • java.lang.String
  • java.lang.Class

Null and Empty Sources

In order to check corner cases and verify proper behavior of our software when it is supplied bad input, it can be useful to have null and empty values supplied to our parameterized tests. The following annotations serve as sources of null and empty values for parameterized tests that accept a single argument.

  • @NullSource: provides a single null argument to the annotated @ParameterizedTest method.
    • @NullSource cannot be used for a parameter that has a primitive type.
  • @EmptySource: provides a single empty argument to the annotated @ParameterizedTest method for parameters of the following types: java.lang.String, java.util.List, java.util.Set, java.util.Map, primitive arrays (e.g., int[], char[][], etc.), object arrays (e.g.,String[], Integer[][], etc.).
    • Subtypes of the supported types are not supported.
  • @NullAndEmptySource: a composed annotation that combines the functionality of @NullSource and @EmptySource.

You can also combine @NullSource, @EmptySource, and @ValueSource to test a wider range of null, empty, and blank input. The following example demonstrates how to achieve this for strings.

@ParameterizedTest
@NullSource
@EmptySource
@ValueSource(strings = { " ", "   ", "\t", "\n" })
void nullEmptyAndBlankStrings(String text) {
    assertTrue(text == null || text.trim().isEmpty());
}

Making use of the composed @NullAndEmptySource annotation simplifies the above as follows.

@ParameterizedTest
@NullAndEmptySource
@ValueSource(strings = { " ", "   ", "\t", "\n" })
void nullEmptyAndBlankStrings(String text) {
    assertTrue(text == null || text.trim().isEmpty());
}

@CsvSource

@CsvSource allows you to express argument lists as comma-separated values (i.e., String literals).

@ParameterizedTest
@CsvSource({
    "apple,         1",
    "banana,        2",
    "'lemon, lime', 0xF1"
})
void testWithCsvSource(String fruit, int rank) {
    assertNotNull(fruit);
    assertNotEquals(0, rank);
}

@CsvSource uses a single quote ' as its quote character. See the 'lemon, lime' value in the example above and in the table below. An empty, quoted value '' results in an empty String unless the emptyValue attribute is set; whereas, an entirely empty value is interpreted as a null reference.

An ArgumentConversionException is raised if the target type of a null reference is a primitive type.

Example InputResulting Argument List
@CsvSource({ "apple, banana" })"apple", "banana"
@CsvSource({ "apple, 'lemon, lime'" })"apple", "lemon, lime"
@CsvSource({ "apple, ''" })"apple", ""
@CsvSource({ "apple, " })"apple", null

@CsvFileSource

@CsvFileSource lets you use CSV files from the classpath. Each line from a CSV file results in one invocation of the parameterized test.

@ParameterizedTest
@CsvFileSource(resources = "/two-column.csv", numLinesToSkip = 1)
void testWithCsvFileSource(String country, int reference) {
    assertNotNull(country);
    assertNotEquals(0, reference);
}

Summery

JUnit Jupiter is more simple than JUnit 4, and the syntax is more concise.

Anyway, the study of JUnit Jupiter is not easy because most references on the Internet or in library are created for JUnit 4. This essay is base on the document of JUnit 5, so there are many similarities between this essay and the document.

At last, this article is writing for myself.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值