“This is the first day of my participation in the August More Text Challenge.
General situation of
JUnit is an excellent open source Java unit testing framework. It is also the most used and most popular testing framework. JUnit is supported by Eclipse and IDEA.
Related words:
Unit testing: The examination and verification of the smallest testable units in software. The unit is the minimum function module under test. Unit testing is the lowest level of testing activity to be performed during software development, where individual units of software are tested in isolation from the rest of the program.
White box testing: White box testing is also called structural testing, transparent box testing, logic-driven testing, or code-based testing. White box testing is a test case design method where the box refers to the software being tested and the white box refers to the box being visible, i.e. it is clear what is inside the box and how it works. The “white box” method has a comprehensive understanding of the internal logical structure of the program and tests all logical paths. The “white box” method is an exhaustive path test. In using this scheme, the tester must examine the internal structure of the program, starting with the logic of the program, and derive test data. The number of independent paths through the program is astronomical.
Regression testing: Regression testing is when you modify old code and re-test it to make sure that the change did not introduce new errors or cause errors in other code. Automated regression testing will significantly reduce the cost of system testing, maintenance and upgrades.
For details, see JUnit 5 Official Documentation
The basic use
Spring Boot itself is integrated with JUnit, as shown in Maven:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
Copy the code
Specific test code can be written in SRC /test/ Java/project name/under the new Java file
package com.example.demo;
import org.junit.jupiter.api.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
// flags the Spring Boot test class
@SpringBootTest
public class DemoTests {
// It must be static before the entire test
@BeforeAll
static void initAll(a){
System.out.println("BeforeAll init succeed!");
}
// Execute before each test method
@BeforeEach
void initEach(a){
System.out.println("-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --");
System.out.println("BeforeEach init succeed!");
}
// Identifies as a test method
@Test
// Test method display name
@DisplayName("java method test demo1")
void calculateTest1(a){
int actual = 1 + 2;
// assert, check whether the value is equal, if so, continue execution
Assertions.assertEquals(3, actual);
System.out.println(actual);
}
@Test
@DisplayName("java method test demo2")
void calculateTest2(a){
int actual = 1 + 2;
/ / assert that determine whether equal, otherwise throw an error org. Opentest4j. An AssertionFailedError is generated
Assertions.assertEquals(5, actual);
System.out.println(actual);
}
// execute after each test method
@AfterEach
void afterEachDestroy(a){
System.out.println("AfterEach destroy succeed!");
System.out.println("-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --");
}
// Execute after the entire test. It must be static
@AfterAll
static void afterAllDestroy(a){
System.out.println("AfterAll destroy succeed!"); }}Copy the code
The running results are as follows:
Demo1:
Demo2:
assertions
When we write code, we always make assumptions, and assertions are used to capture those assumptions in our code. The programmer believes that the expression value is true at a particular point in the program and that assertion validation can be enabled and disabled at any time, so assertions can be enabled at test time and disabled at deployment time. Similarly, after the program is up and running, end users can re-enable assertions if they encounter problems.
Using assertions creates more stable, better quality, and less error-prone code. Assertions are used when you need to interrupt the current operation when a value is FALSE. Unit tests must use assertions (Junit/JunitX).
The specific usage methods are as follows. In addition to assertEquals(), assertAll(), assertTimeout() and other methods, see the official documents for details.
// Identifies as a test method
@Test
// Test method display name
@DisplayName("java method test demo1")
void calculateTest1(a){
int actual = 1 + 2;
// assert, check whether the value is equal, if so, continue execution
Assertions.assertEquals(3, actual);
System.out.println(actual);
}
@Test
@DisplayName("java method test demo2")
void calculateTest2(a){
int actual = 1 + 2;
/ / assert that determine whether equal, otherwise throw an error org. Opentest4j. An AssertionFailedError is generated
Assertions.assertEquals(5, actual);
/ / does not perform
System.out.println(actual);
}
Copy the code
Assuming that
In the org. Junit. Jupiter. API. Assumptions, encapsulates a set of methods, to support the test execution based on the assumption of conditions.
The hypothesis is essentially specifying a particular condition, and if the hypothesis cannot be met, the hypothesis does not cause the test to fail, but simply terminates the current test. This is also the biggest difference between a hypothesis and an assertion, because in the case of an assertion, the test will fail.
Personal understanding assertions are generally used to test whether the result is correct, while assumptions emphasize whether the preconditions of the test are correct. If not, the test is not necessary to continue, i.e. skip the test.
package com.example.demo;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import static org.junit.jupiter.api.Assumptions.assumeTrue;
@SpringBootTest
public class AssumeTest {
@Test
@DisplayName("assumeTestDemo1")
void assumeTestDemo1(a){
assumeTrue(true);
System.out.println("assume true!");
}
@Test
@DisplayName("assumeTestDemo2")
void assumeTestDemo2(a){
System.out.println("assume false!");
assumeTrue(false);
System.out.println("assume false!"); }}Copy the code
The running results are as follows:
Not:
Demo2:
Test one more piece of code to deepen your understanding.
package com.example.demo;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import static org.junit.jupiter.api.Assumptions.assumeTrue;
@SpringBootTest
public class AssumeTest {
@BeforeAll
static void beforeAssumeTest(a){
assumeTrue(false);
}
@Test
@DisplayName("assumeTestDemo1")
void assumeTestDemo1(a){
assumeTrue(true);
System.out.println("assume true!");
}
@Test
@DisplayName("assumeTestDemo2")
void assumeTestDemo2(a){
assumeTrue(false);
System.out.println("assume false!"); }}Copy the code
The result is as follows:
Same with @beforeeach.
disable
Disabled is implemented with the @disabled annotation, which can be used to test classes and test methods. Marked classes or methods will not be executed.
package com.example.demo;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
public class DisabledTest {
@Disabled
@Test
void disabledTestDemo1(a){
System.out.println("disable1");
}
@Test
void disabledTestDemo2(a){
System.out.println("disable2"); }}Copy the code
The result is as follows:
The label
JUnit can test by Tag with the @tag annotation.
Example code is as follows:
TagTest1.java
@SpringBootTest
public class TagTest1 {
@Test
@Tag("hello")
void TagTest1(a){
System.out.println("hello1");
}
@Test
@Tag("hi")
void TagTest2(a){
System.out.println("hi1"); }}Copy the code
TagTest2.java
@SpringBootTest
public class TagTest2 {
@Test
@Tag("hello")
void TagTest3(a){
System.out.println("hello2");
}
@Test
@Tag("hi")
void TagTest4(a){
System.out.println("hi2"); }}Copy the code
Filter label hello. The result is as follows:
See that only test methods labeled “Hello” are executed.
Test the instance life cycle
JUnit 5’s default test instance life cycle is per-method, which means that for each test method, a test class is re-instantiated. This is why the @beforeAll and @Afterall embellished methods need to be static.
However, the Lifecycle of a TestInstance can be modified to execute all test methods on the same TestInstance via @testinstance (Lifecycle.PER_CLASS). When using this pattern, a new test instance is created for each test class. Therefore, if your test method depends on state stored in instance variables, you may need to reset that state in the @beforeeach or @Aftereach methods.
Nested test
When a test class has a large number of functions to test, the structure of the test class becomes complex. At this point, we can optimize the structure of the test class with annotations @nested and inner class forms to make the test presentation clearer
@DisplayName("A stack")
public class TestingAStackDemo {
Stack<Object> stack;
@Test
@DisplayName("is instantiated with new Stack()")
void isInstantiatedWithNew(a) {
new Stack<>();
}
@Nested
@DisplayName("when new")
class WhenNew {
@BeforeEach
void createNewStack(a) {
stack = new Stack<>();
}
@Test
@DisplayName("is empty")
void isEmpty(a) {
assertTrue(stack.isEmpty());
}
@Test
@DisplayName("throws EmptyStackException when popped")
void throwsExceptionWhenPopped(a) {
assertThrows(EmptyStackException.class, () -> stack.pop());
}
@Test
@DisplayName("throws EmptyStackException when peeked")
void throwsExceptionWhenPeeked(a) {
assertThrows(EmptyStackException.class, () -> stack.peek());
}
@Nested
@DisplayName("after pushing an element")
class AfterPushing {
String anElement = "an element";
@BeforeEach
void pushAnElement(a) {
stack.push(anElement);
}
@Test
@DisplayName("it is no longer empty")
void isNotEmpty(a) {
assertFalse(stack.isEmpty());
}
@Test
@DisplayName("returns the element when popped and is empty")
void returnElementWhenPopped(a) {
assertEquals(anElement, stack.pop());
assertTrue(stack.isEmpty());
}
@Test
@DisplayName("returns the element when peeked but remains not empty")
void returnElementWhenPeeked(a) { assertEquals(anElement, stack.peek()); assertFalse(stack.isEmpty()); }}}}Copy the code
The result is as follows:
Repeat the test
Repeated testing is possible with the @repeattest annotation.
Example code is as follows:
public class RepeatTest {
// Execute the test method 10 times
@RepeatedTest(10)
void repeatTest(a){
System.out.println("Hello world!"); }}Copy the code
The result is as follows:
Conditions for testing
JUnit 5 supports condition testing. Common condition annotations are @enabledif, @disabledif, and built-in condition @enabledonos.
Example code is as follows:
public class IfTest {
@Test
@EnabledIf("getCondition")
void enabledIfTest(a) {
System.out.println("Hello world1");
}
@Test
@DisabledIf("getCondition")
void disabledIfTest(a){
System.out.println("Hello world2");
}
boolean getCondition(a){
return true; }}Copy the code
The result is as follows:
Parametric test
Parameterized testing is the execution of a test method using different parameters each time, with support for injection of array and Enum parameters, using annotations @Valuesource and EnumSource, respectively. Note that instead of using the @test annotation, the Test method needs to use the @parameterizedTest annotation.
Example code is as follows:
public class ParameterTest {
@ParameterizedTest
@ValueSource(strings = {"Lei Li","Meimei Han"})
void parameterTest(String word){
System.out.println("Hello,"+word); }}Copy the code
The result is as follows:
Advanced usage
Web service Testing
When annotating @SpringBooTtest on the test class, the SpringBoot environment will be automatically initialized during testing, and the Web service can be tested by instantiating the TestRestTemplate object.
Example code is as follows:
HelloController.java
@RestController
public class HelloController {
@RequestMapping(value = "/hello",method = RequestMethod.GET)
public Object hello(a){
return "hello world!"; }}Copy the code
HelloControllerTests.java
// Indicates that the test environment is a random port
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class HelloControllerTests {
@Autowired
private TestRestTemplate restTemplate;
@Test
@DisplayName("Web service test demo")
void helloTest(a){
String object = restTemplate.getForObject("/hello", String.class);
System.out.println(object);
Assertions.assertEquals("hello world!",object); }}Copy the code
The test results are as follows:
Database testing
When the test method is annotated with @Transaction, the database operations in the test method will be rolled back by default without contaminating the database.
Example code is as follows:
package com.example.demo;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.example.demo.dto.User;
import com.example.demo.mapper.UserMapper;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.transaction.annotation.Transactional;
/ * * *@description:
* @time: 2021/8/4 16:15 * /
@SpringBootTest
public class DatabaseTest {
@Autowired
UserMapper userMapper;
@Test
@Transactional
void databaseInsertTest(a){
User entity = new User();
entity.setUserName("San Zhang");
entity.setUserId(2L);
userMapper.insert(entity);
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
User user = userMapper.selectById(3);
Assertions.assertEquals(null,user); }}Copy the code
The result is as follows:
See no actual insert in database table.