image-20210517194801837

参考资料

  1. JUnit Tutorial for Beginners: Learn in 3 Days
  2. junit4 github
  3. Unit Testing with JUnit 5 - Tutorial
  4. JUnit 5 User Guide

JUnit4

package: org.Junit

Concepts

Test Case

A test case is a set of actions executed to verify a particular feature or functionality of your software application. A Test Case contains test steps, test data, precondition, postcondition developed for specific test scenario to verify any requirement.

For a Test Scenario: Check Login Functionality there many possible test cases are:

  • Test Case 1: Check results on entering valid User Id & Password
  • Test Case 2: Check results on entering Invalid User ID & Password
  • Test Case 3: Check response when a User ID is Empty & Login Button is pressed, and many more

Test fixture

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class OutputFileTest {
private File output;
@Before
public void createOutputFile() {
output = new File(...);
}

true@After
public void deleteOutputFile() {
output.delete();
}

@Test
public void testFile1() {
// code for test case objective
}
true@Test
public void testFile2() {
// code for test case objective
}
}

A test fixture is a context where a Test Case runs. Typically, test fixtures include:

  • Objects or resources that are available for any test case.
  • Activities required that makes these objects/resources available, such as setup and teardown.

Test Suites

Test suite allows us to aggregate all test cases from multiple classes in an order in one place and run it together.

To run the suite test, you need to annotate a class using below-mentioned annotations:

@Runwith(Suite.class)

@SuiteClasses(test1.class,test2.class……) or @Suite.SuiteClasses ({test1.class, test2.class……})

例子:

Create JUnit Test Suite

Create JUnit Test Suite

Test Runner

a tool for execution of the test cases.

1
2
3
4
5
6
7
8
9
public class Test {                
truepublic static void main(String[] args) {
truetrueResult result = JUnitCore.runClasses(CreateAndSetName.class);
for (Failure failure : result.getFailures()) {
System.out.println(failure.toString());
}
System.out.println(result.wasSuccessful());
}
}

Test Scenario

A test scenario is defined as any functionality that can be tested. It is also called Test Condition or Test Possibility.

Error Collector

With JUnit error collector, you can still continue with the test execution even after an issue is found or test fails. Error collector collects all error objects and reports it only once after the test execution is over.

Annotations

@Before and @After

@Before annotation is used on a method containing code to run before each test case. i.e it runs before each test execution. @After annotation is used on a method containing java code to run after each test case. These methods will run even if any exceptions are thrown in the test case or in the case of assertion failures.

  • All the methods annotated with @Before will run before each test case, but they may run in any order.
  • You can inherit @Before and @After methods from a super class, Execution is as follows: It is a standard execution process in JUnit.
  1. Execute the @Before methods in the superclass
  2. Execute the @Before methods in this class
  3. Execute a @Test method in this class
  4. Execute the @After methods in this class
  5. Execute the @After methods in the superclass

@BeforeClass and @AfterClass

Run a method only once for the entire test class before(after) any of the tests are executed. They are used for “once-only setup“ and “once-only teardown“, such as start & stop servers and open & close communication.

Annotations Description
@Test This annotation is a replacement of org.junit.TestCase which indicates that public void method to which it is attached can be executed as a test Case.
@Ignore This annotation can be used if you want to ignore some statements during test execution for e.g. disabling some test cases during test execution.
@Test(timeout=500) This annotation can be used if you want to set some timeout during test execution for e.g. if you are working under some SLA (Service level agreement), and tests need to be completed within some specified time.
@Test(expected=IllegalArgumentException.class) This annotation can be used if you want to handle some exception during test execution. For, e.g., if you want to check whether a particular method is throwing specified exception or not.

@Ignore

can be used in two scenarios as given below:

  1. If you want to ignore a test method, use @Ignore along with @Test annotation.
  2. If you want to ignore all the tests of class, use @Ignore annotation at the class level.

For exception testing, you can use

  • Optional parameter (expected) of @test annotation and
  • To trace the information ,”fail()” can be used

@Rule

is used to create an object of error collector. Once the object for error collector is created, you can easily add all the errors into the object using method addError (Throwable error), these errors will be logged in JUnit test result.

example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package guru99.junit;        

import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ErrorCollector;

public class ErrorCollectorExample {
@Rule
public ErrorCollector collector = new ErrorCollector();
@Test
public void example() {
collector.addError(new Throwable("There is an error in first line"));
collector.addError(new Throwable("There is an error in second line"));
System.out.println("Hello");
try {
Assert.assertTrue("A " == "B");
} catch (Throwable t) {
collector.addError(t);
}
System.out.println("World!!!!");
}
}

JUnit ErrorCollector

@Parameters

Parameterized test is to execute the same test over and over again using different values. It helps developer to save time in executing same test which differs only in their inputs and expected results. Using Parameterized test, one can set up a test method that retrieves data from some data source.

example:

  1. Create a parameterized test class

JUnit Parameterized Test

@RunWith annotation is used to specify its runner class name. If we don’t specify any type as a parameter, the runtime will choose BlockJunit4ClassRunner by default.

  1. Create a constructor that stores the test data. It stores 3 variables

    JUnit Parameterized Test

  2. Create a static method that generates and returns test data.

JUnit Parameterized Test

JUnit Parameterized Test

  1. Create a test runner class to run parameterized test

JUnit Parameterized Test

Assertions

  • org.junit.Assert
Method Description
void assertEquals(boolean expected, boolean actual) It checks whether two values are equals similar to equals method of Object class
void assertFalse(boolean condition) functionality is to check that a condition is false.
void assertNotNull(Object object) “assertNotNull” functionality is to check that an object is not null.
void assertNull(Object object) “assertNull” functionality is to check that an object is null.
void assertTrue(boolean condition) “assertTrue” functionality is to check that a condition is true.
void fail() If you want to throw any assertion error, you have fail() that always results in a fail verdict.
void assertSame([String message] “assertSame” functionality is to check that the two objects refer to the same object.
void assertNotSame([String message] “assertNotSame” functionality is to check that the two objects do not refer to the same object.
  • assertArrayEquals(expected, actual): if arrays have the same length
  • assertEquals( aDoubleValue, anotherDoubleValue, 0.001 ): Math.abs( expected – actual ) <= delta

Class and Methods

org.junit.TestCase

Method Description
int countTestCases() This method is used to count how many number of test cases executed by run(TestResult tr) method.
TestResult createResult() This method is used to create a TestResult object.
String getName() This method returns a string which is nothing but a TestCase.
TestResult run() This method is used to execute a test which returns a TestResult object
void run(TestResult result) This method is used to execute a test having a TestResult object which doesn’t returns anything.
void setName(String name) This method is used to set a name of a TestCase.
void setUp() This method is used to write resource association code. e.g. Create a database connection.
void tearDown() This method is used to write resource release code. e.g. Release database connection after performing transaction operation.

org.junit.TestResult

Method Description
void addError(Test test, Throwable t) This method is used if you require add an error to the test.
void addFailure(Test test, AssertionFailedError t) This method is used if you require add a failure to the list of failures.
void endTest(Test test) This method is used to notify that a test is performed(completed)
int errorCount() This method is used to get the error detected during test execution.
Enumeration<TestFailure> errors() This method simply returns a collection (Enumeration here) of errors.
int failureCount() This method is used to get the count of errors detected during test execution.
void run(TestCase test) This method is used to execute a test case.
int runCount() This method simply counts the executed test.
void startTest(Test test) This method is used to notify that a test is started.
void stop() This method is used to test run to be stopped.

org.junit.TestSuite

Method Description
void addTest(Test test) This method is used if you want to add a test to the suite.
void addTestSuite(Class<? extends TestCase> testClass) This method is used if you want to specify the class while adding a test to the suite.
int countTestCases() This method is used if you want to count the number of test cases.
String getName() This method is used to get the name of the test suite.
void run(TestResult result) This method is used to execute a test and collect test result in TestResult object.
void setName(String name) This method is used to set the name of TestSuite.
Test testAt(int index) This method is used if you want to return the test at given index.
int testCount() This method is used if you want to return a number of tests in the Suite.
static Test warning(String message) This method returns a test which will fail and log a warning message.

JUnit 5

JUnit 5 = JUnit Platform + JUnit Jupiter + JUnit Vintage

Annotations

Unless otherwise stated, all core annotations are located in the org.junit.jupiter.api package in the junit-jupiter-api module.

Annotation Description
@Test Denotes that a method is a test method. Unlike JUnit 4’s @Test annotation, this annotation does not declare any attributes, since test extensions in JUnit Jupiter operate based on their own dedicated annotations. Such methods are inherited unless they are overridden.
@ParameterizedTest Denotes that a method is a parameterized test. Such methods are inherited unless they are overridden.
@RepeatedTest Denotes that a method is a test template for a repeated test. Such methods are inherited unless they are overridden.
@TestFactory Denotes that a method is a test factory for dynamic tests. Such methods are inherited unless they are overridden.
@TestTemplate Denotes that a method is a template for test cases designed to be invoked multiple times depending on the number of invocation contexts returned by the registered providers. Such methods are inherited unless they are overridden.
@TestMethodOrder Used to configure the test method execution order for the annotated test class; similar to JUnit 4’s @FixMethodOrder. Such annotations are inherited.
@TestInstance Used to configure the test instance lifecycle for the annotated test class. Such annotations are inherited.
@DisplayName Declares a custom display name for the test class or test method. Such annotations are not inherited.
@DisplayNameGeneration Declares a custom display name generator for the test class. Such annotations are inherited.
@BeforeEach Denotes that the annotated method should be executed before each @Test, @RepeatedTest, @ParameterizedTest, or @TestFactory method in the current class; analogous to JUnit 4’s @Before. Such methods are inherited unless they are overridden.
@AfterEach Denotes that the annotated method should be executed after each @Test, @RepeatedTest, @ParameterizedTest, or @TestFactory method in the current class; analogous to JUnit 4’s @After. Such methods are inherited unless they are overridden.
@BeforeAll Denotes that the annotated method should be executed before all @Test, @RepeatedTest, @ParameterizedTest, and @TestFactory methods in the current class; analogous to JUnit 4’s @BeforeClass. Such methods are inherited (unless they are hidden or overridden) and must be static (unless the “per-class” test instance lifecycle is used).
@AfterAll Denotes that the annotated method should be executed after all @Test, @RepeatedTest, @ParameterizedTest, and @TestFactory methods in the current class; analogous to JUnit 4’s @AfterClass. Such methods are inherited (unless they are hidden or overridden) and must be static (unless the “per-class” test instance lifecycle is used).
@Nested Denotes that the annotated class is a non-static nested test class. @BeforeAll and @AfterAll methods cannot be used directly in a @Nested test class unless the “per-class” test instance lifecycle is used. Such annotations are not inherited.
@Tag Used to declare tags for filtering tests, either at the class or method level; analogous to test groups in TestNG or Categories in JUnit 4. Such annotations are inherited at the class level but not at the method level.
@Disabled Used to disable a test class or test method; analogous to JUnit 4’s @Ignore. Such annotations are not inherited.
@Timeout Used to fail a test, test factory, test template, or lifecycle method if its execution exceeds a given duration. Such annotations are inherited.
@ExtendWith Used to register extensions declaratively. Such annotations are inherited.
@RegisterExtension Used to register extensions programmatically via fields. Such fields are inherited unless they are shadowed.
@TempDir Used to supply a temporary directory via field injection or parameter injection in a lifecycle method or test method; located in the org.junit.jupiter.api.io package.

Assertions

JUnit Jupiter comes with many of the assertion methods that JUnit 4 has and adds a few that lend themselves well to being used with Java 8 lambdas. All JUnit Jupiter assertions are static methods in the org.junit.jupiter.api.Assertions class.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
import static java.time.Duration.ofMillis;
import static java.time.Duration.ofMinutes;
import static org.junit.jupiter.api.Assertions.assertAll;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTimeout;
import static org.junit.jupiter.api.Assertions.assertTimeoutPreemptively;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.util.concurrent.CountDownLatch;

import example.domain.Person;
import example.util.Calculator;

import org.junit.jupiter.api.Test;

class AssertionsDemo {

private final Calculator calculator = new Calculator();

private final Person person = new Person("Jane", "Doe");

@Test
void standardAssertions() {
assertEquals(2, calculator.add(1, 1));
assertEquals(4, calculator.multiply(2, 2),
"The optional failure message is now the last parameter");
assertTrue('a' < 'b', () -> "Assertion messages can be lazily evaluated -- "
+ "to avoid constructing complex messages unnecessarily.");
}

@Test
void groupedAssertions() {
// In a grouped assertion all assertions are executed, and all
// failures will be reported together.
assertAll("person",
() -> assertEquals("Jane", person.getFirstName()),
() -> assertEquals("Doe", person.getLastName())
);
}

@Test
void dependentAssertions() {
// Within a code block, if an assertion fails the
// subsequent code in the same block will be skipped.
assertAll("properties",
() -> {
String firstName = person.getFirstName();
assertNotNull(firstName);

// Executed only if the previous assertion is valid.
assertAll("first name",
() -> assertTrue(firstName.startsWith("J")),
() -> assertTrue(firstName.endsWith("e"))
);
},
() -> {
// Grouped assertion, so processed independently
// of results of first name assertions.
String lastName = person.getLastName();
assertNotNull(lastName);

// Executed only if the previous assertion is valid.
assertAll("last name",
() -> assertTrue(lastName.startsWith("D")),
() -> assertTrue(lastName.endsWith("e"))
);
}
);
}

@Test
void exceptionTesting() {
Exception exception = assertThrows(ArithmeticException.class, () ->
calculator.divide(1, 0));
assertEquals("/ by zero", exception.getMessage());
}

@Test
void timeoutNotExceeded() {
// The following assertion succeeds.
assertTimeout(ofMinutes(2), () -> {
// Perform task that takes less than 2 minutes.
});
}

@Test
void timeoutNotExceededWithResult() {
// The following assertion succeeds, and returns the supplied object.
String actualResult = assertTimeout(ofMinutes(2), () -> {
return "a result";
});
assertEquals("a result", actualResult);
}

@Test
void timeoutNotExceededWithMethod() {
// The following assertion invokes a method reference and returns an object.
String actualGreeting = assertTimeout(ofMinutes(2), AssertionsDemo::greeting);
assertEquals("Hello, World!", actualGreeting);
}

@Test
void timeoutExceeded() {
// The following assertion fails with an error message similar to:
// execution exceeded timeout of 10 ms by 91 ms
assertTimeout(ofMillis(10), () -> {
// Simulate task that takes more than 10 ms.
Thread.sleep(100);
});
}

@Test
void timeoutExceededWithPreemptiveTermination() {
// The following assertion fails with an error message similar to:
// execution timed out after 10 ms
assertTimeoutPreemptively(ofMillis(10), () -> {
// Simulate task that takes more than 10 ms.
new CountDownLatch(1).await();
});
}

private static String greeting() {
return "Hello, World!";
}

}

Assumptions

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assumptions.assumeTrue;
import static org.junit.jupiter.api.Assumptions.assumingThat;

import example.util.Calculator;

import org.junit.jupiter.api.Test;

class AssumptionsDemo {

private final Calculator calculator = new Calculator();

@Test
void testOnlyOnCiServer() {
assumeTrue("CI".equals(System.getenv("ENV")));
// remainder of test
}

@Test
void testOnlyOnDeveloperWorkstation() {
assumeTrue("DEV".equals(System.getenv("ENV")),
() -> "Aborting test: not on developer workstation");
// remainder of test
}

@Test
void testInAllEnvironments() {
assumingThat("CI".equals(System.getenv("ENV")),
() -> {
// perform these assertions only on the CI server
assertEquals(2, calculator.divide(4, 2));
});

// perform these assertions in all environments
assertEquals(42, calculator.multiply(6, 7));
}

}

Migrating from JUnit4

  • Annotations reside in the org.junit.jupiter.api package.
  • Assertions reside in org.junit.jupiter.api.Assertions.
    • Note that you may continue to use assertion methods from org.junit.Assert or any other assertion library such as AssertJ, Hamcrest, Truth, etc.
  • Assumptions reside in org.junit.jupiter.api.Assumptions.
    • Note that JUnit Jupiter 5.4 and later versions support methods from JUnit 4’s org.junit.Assume class for assumptions. Specifically, JUnit Jupiter supports JUnit 4’s AssumptionViolatedException to signal that a test should be aborted instead of marked as a failure.
  • @Before and @After no longer exist; use @BeforeEach and @AfterEach instead.
  • @BeforeClass and @AfterClass no longer exist; use @BeforeAll and @AfterAll instead.
  • @Ignore no longer exists: use @Disabled or one of the other built-in execution conditions instead.
  • @Category no longer exists; use @Tag instead.
  • @RunWith no longer exists; superseded by @ExtendWith.
  • @Rule and @ClassRule no longer exist; superseded by @ExtendWith and @RegisterExtension

留言

⬆︎TOP