Diary of JUnit Learning

mac2024-08-19  63

Diary of JUnit Learning

Author:Roser Han

Location:NUAA

文章目录

Diary of JUnit LearningAuthor:Roser HanLocation:NUAA Before ReadingWriting TestsTest Classes and MethodsDisplay NamesAssertionsThird-party Assertion Libraries AssumptionsDisabling TestsConditional Test ExecutionScript-based Conditions TaggingParameterized TestsSources of Arguments Summery

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:

value of the @DisplayName annotation, if presentby calling the DisplayNameGenerator specified in the @DisplayNameGeneration annotation, if presentby calling the default DisplayNameGenrator configured via the configuration parameter, if presentby 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

AssertionDescriptionfail(String message)Fail the test with the given failure messageassertTrue(boolean condition)Assert that the supplied condition is trueassertFalse(boolean condition)Assert that the supplied condition is falseassertNull(Object actual, [String message])Assert that actual is nullFails with the supplied failure messageassertNotNull(Object actual, [String message])Assert that actual is not nullFails with the supplied failure messageassertEquals(expected, actual)This assertion is the most frequently-used one.Assert that expected and actual are equalassertArrayEquals(TypeName[] expeted, TypeName[] actual)Assert that expected and actual array are equal(content)assertNotEquals(expect, actual)Assert that expected and actual are not equalassertSame(Object expected, Object actual)Assert that expected and actual refer to the same objectassertNotSame(Object expected, Object actual)Assert that expected and actual refer to the different objectassertAll(excutables) throws MutipleFailuresErrorAssert that all supplied executables do not throw exceptionsassertThrows(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.

AssumptionDescriptionassumeTrue(assumption)Validate the given assumptionassumeFalse(assumption)Validate the given assumptionassumingThat(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.

shortbyteintlongfloatdoublecharbooleanjava.lang.Stringjava.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)