Analysis of Java unit testing (JUnit+Mockito)

1. What is unit testing

(1) Unit testing link:

The testing process is divided into phases: unit testing, integration testing, system testing, acceptance testing, etc. The relevant meanings are as follows:

1) Unit test: To check the output correctness of computer program modules.

2) Integration testing: On the basis of unit testing, each module is integrated to form a subsystem for integration testing.

3) System testing: Take into account the collaboration content involved in the entire delivery, including a series of computer hardware, software, interface, operation, etc. as a whole, to verify whether the software or requirement specification is met.

4) Acceptance testing: Test and verify the work done before delivery or release.

Unit testing is the primary link of phased testing, and it is also a type of white-box testing. The writing and practice of this content can be completed in advance of R&D. When R&D writes business code, it needs to generate unit tests for the corresponding code. The initiator of unit testing is the program designer, and the beneficiary is also the person who writes the program. Therefore, it is very necessary for programmers to form self-discipline and complete the basic unit test case writing.

(2) Unit test features:

It can be seen from the above that the unit test is actually verified for the smallest test unit in the software. The unit here refers to a subset of related functions, such as a method, a class, and so on. It is worth noting that as the lowest level of testing activities, the object of unit test verification is limited to the current test content and is isolated from other parts of the program. In summary, unit testing has the following characteristics:

1) The main function is to prove that the written code content is consistent with the expected output.

2) The smallest and lowest-level test content is initiated by the programmer himself to ensure that the basic components of the program are normal.

3) Try not to distinguish between classes and methods in unit testing. It is advocated to use procedural methods as the test unit, and aim to be simple, practical and efficient.

4) Don't deviate from the topic, focus on testing a small piece of code to ensure basic functionality.

5) Strip the dependencies with external interfaces and storage to make unit testing controllable.

6) Any order in which the unit tests are executed at any time needs to be successful.

2. Why unit test

(1) Meaning of unit testing:

Program codes are composed of basic units into complex systems, and the underlying basic units cannot guarantee the correctness of input and output. As the levels increase, the problem will continue to magnify until the entire system crashes and cannot be used. So the significance of unit testing is to ensure that the basic functions are normally available and stable. The unstable factors caused by interfaces, data sources, etc. are external reasons and are not considered within the scope of unit testing.

(2) Use the main method for testing:

@PostMapping(value="/save")
public Map<String,Object> save(@RequestBody Student stu) {
    studentService.save(stu);
    Map<String,Object> params = new HashMap<>();
    params.put("code",200);
    params.put("message","保存成功");
    return params;
}

If you want to test the above Controller, you can write the following code example. When using the main method to test, first start the entire project application, and then write the main method as follows to access and debug the code in a single step.

public static void main(String[] args) {
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        String json = "{"name":"张三","className":"三年级一班","age":"20","sex":"男"}";
        HttpEntity<String> httpEntity = new HttpEntity<>(json, headers);
        String url = "http://localhost:9092/student/save";
        MainMethodTest test = new MainMethodTest();
        ResponseEntity<Map> responseEntity = test.getRestTemplate().postForEntity(url, httpEntity, Map.class);
        System.out.println(responseEntity.getBody());
    }

(3) Disadvantages of using the main method for testing:

1) Printing out each content to the console by writing a large number of main methods is boring and cumbersome, not elegant.

2) The test methods cannot be run together, and the result requires the programmer to judge the correctness by himself.

3) Unified and repetitive work should be handed over to tools.

3. Unit testing framework - JUnit

3.1 Introduction to JUnit

JUnit official website: https://junit.org/ . JUnit is a simple framework for writing repeatable tests. It is an instance of the xUnit architecture for unit testing frameworks.

Features of JUnit:

(1) A unit testing framework designed specifically for the Java language is widely used.

(2) A standard testing framework for a specific domain.

(3) It can be used in various IDE development platforms, including Idea and Eclipse for integration.

(4) It can be easily introduced and used by Maven.

(5) It is convenient to write unit test codes, view test results, etc.

Important concepts of JUnit:

name function
Assert Assertion method set
TestCase represents a test case
TestSuite Contains a set of TestCase to form a set of tests
TestResult Collect test results

Some notes and specifications of JUnit:

(1) The test method must be   modified with @Test

(2) The test method must be modified with public void and cannot take parameters

(3) The package of the test code should be consistent with the package structure of the tested code

(4) Each method in the test unit must be independently testable, without any dependencies between methods

(5) Test classes generally use Test as the suffix of the class name

(6) The test method generally uses test as the prefix of the method name

JUnit failure result description:

(1) Failure: The test result is inconsistent with the expected result, indicating that the test fails

(2) error: caused by the abnormal code, it can be caused by the error of the test code itself, or it can be a bug of the tested code

3.2 JUnit content

(1) Assertion API

Assertion method assertion description
assertNull(String message, Object object) Check whether the object is empty, and report an error if it is not empty
assertNotNull(String message, Object object) Check whether the object is not empty, and report an error if it is empty
assertEquals(String message, Object expected, Object actual) Check if the object values ​​are equal, and report an error if they are not equal
assertTrue(String message, boolean condition) Check if the condition is true, and report an error if it is not true
assertFalse(String message, boolean condition) Check if the condition is false, report an error if it is true
assertSame(String message, Object expected, Object actual) Check whether the object references are equal, and report an error if they are not equal
assertNotSame(String message, Object unexpected, Object actual) Check whether the object references are not equal, and report an error if they are equal
assertArrayEquals(String message, Object[] expecteds, Object[] actuals) Check whether the array values ​​are equal, traverse the comparison, and report an error if they are not equal
assertArrayEquals(String message, Object[] expecteds, Object[] actuals) Check whether the array values ​​are equal, traverse the comparison, and report an error if they are not equal
assertThat(String reason, T actual, Matcher<? super T> matcher) Check whether the object satisfies the given rules, and report an error if it does not

(2) JUnit common annotations:

1)  @Test : Define a test method @Test (excepted=xx.class): xx.class indicates the exception class, which means that when the test method throws this exception, it is considered to be a normal test @Test (timeout = milliseconds ) : Test method execution time as expected.

2) @BeforeClass: Executed before all methods are executed, the static method will only be executed once globally, and it will be the first to run.

3) @AfterClass: Execute after all methods are executed, the static method will only be executed once globally, and the last one will be executed.

4) @Before: Executed once before each test method is run.

5) @After: Executed once after each test method runs.

6) @Ignore: The modified test method will be ignored by the test runner.

7) @RunWith: You can change the test executor to use the junit test executor.

3.3 Use of JUnit

3.3.1 Controller layer unit test

(1) It is very simple to use maven to introduce Junit in Springboot, and it can be imported by using the following dependencies:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>

(2) The above case of using the main method can be completed using the following Junit code:

@RunWith(SpringRunner.class)
@SpringBootTest(classes = MainApplication.class)
public class StudentControllerTest {
	
	// 注入Spring容器
    @Autowired
    private WebApplicationContext applicationContext;
    // 模拟Http请求
    private MockMvc mockMvc;

    @Before
    public void setupMockMvc(){
    	// 初始化MockMvc对象
        mockMvc = MockMvcBuilders.webAppContextSetup(applicationContext).build();
    }
    
    /**
     * 新增学生测试用例
     * @throws Exception
     */
    @Test
    public void addStudent() throws Exception{
        String json="{"name":"张三","className":"三年级一班","age":"20","sex":"男"}";
        mockMvc.perform(MockMvcRequestBuilders.post("/student/save")    //构造一个post请求
                    // 发送端和接收端数据格式
                    .contentType(MediaType.APPLICATION_JSON_UTF8)
                    .accept(MediaType.APPLICATION_JSON_UTF8)
                    .content(json.getBytes())
            )
           // 断言校验返回的code编码
           .andExpect(MockMvcResultMatchers.status().isOk())
           // 添加处理器打印返回结果
           .andDo(MockMvcResultHandlers.print());
    }
}

You only need to right-click on the class or the specified method to execute it, and you can directly act as a postman to access the specified url, and you don't need to write request codes, all of which are automatically completed by the tool.

(3) Introduction of relevant components in the case

When constructing a mockMVC object in this case, the following methods can also be used:

@Autowired
private StudentController studentController;
@Before
public void setupMockMvc(){
   // 初始化MockMvc对象
   mockMvc = MockMvcBuilders.standaloneSetup(studentController).build();
}

Among them, MockMVC is a tool provided by the Spring test framework for REST requests. It is a simulation of Http requests. It can call the Controller layer without starting the entire module. It is fast and does not depend on the network environment.

The basic steps to use MockMVC are as follows:

  1. mockMvc.perform execution request
  2. MockMvcRequestBuilders.post or get construction request
  3. MockHttpServletRequestBuilder.param or content add request parameters
  4. MockMvcRequestBuilders.contentType add request type
  5. MockMvcRequestBuilders.accept adds response type
  6. ResultActions.andExpect adds result assertions
  7. ResultActions.andDo adds post-processing of returned results
  8. ResultActions.andReturn returns the corresponding result after execution

3.3.2 Service layer unit test

You can write the following code to perform a single test on the query method of the Service layer:

@RunWith(SpringRunner.class)
@SpringBootTest
public class StudentServiceTest {

	@Autowired
    private StudentService studentService;

    @Test
    public void getOne() throws Exception {
    	 Student stu = studentService.selectByKey(5);
         Assert.assertThat(stu.getName(),CoreMatchers.is("张三"));
    }
}

Results of the:

3.3.3 Dao layer unit test

You can write the following code to test the Dao layer save method:

@RunWith(SpringRunner.class)
@SpringBootTest
public class StudentDaoTest {

	@Autowired
    private StudentMapper studentMapper;

    @Test
    @Rollback(value = true)
    @Transactional
    public void insertOne() throws Exception {
    	 Student student = new Student();
    	 student.setName("李四");
    	 student.setMajor("计算机学院");
    	 student.setAge(25);
    	 student.setSex('男');
    	 int count = studentMapper.insert(student);
    	 Assert.assertEquals(1, count);
    }
}

Among them, @Rollback (value = true) can roll back the newly added data after executing the unit test, so as to keep the database from generating dirty data.

3.3.4 Exception testing

(1) Define an exception in the service layer:

public void computeScore() {
   int a = 10, b = 0;
}

(2) Define the unit test method in the service test class:

@Test(expected = ArithmeticException.class)
    public void computeScoreTest() {
        studentService.computeScore();
    }

(3) Executing the unit test will also pass, because the exception is defined in the @Test annotation

3.3.5 Test suites test multiple classes

(1) Create a new empty unit test class

(2) Use the annotations @RunWith (Suite.class) and @SuiteClasses to mark the classes to be unit tested together

@RunWith(Suite.class)
@Suite.SuiteClasses({ StudentServiceTest.class, StudentDaoTest.class})
public class AllTest {
}

operation result:

3.3.6 View unit test coverage in idea

(1) Single test coverage

Test coverage is an indicator to measure the effectiveness of the test process itself, improve test efficiency and reduce program bugs, and improve product reliability and stability.

The significance of statistical unit test coverage:

1) It can gain insight into all blind spots of basic component functions in the entire code and find related problems.

2) To improve the code quality, usually a low coverage rate means that the code quality will not be too high, because the failure of the single test means that it is not enough to consider various situations.

3) The code design ability can be improved from the coverage rate.

(2) It is very simple to view the unit test coverage in the idea, just run according to the icon in the example in the figure, or right-click Run 'xxx' with Coverage on the unit test method or class. The execution result is a table listing classes, methods, line numbers, and branch coverage.

(3) The coverage will be identified in the code, the green ones are covered, and the red ones are not covered.

(4) If you want to export the coverage results of the unit test, you can use the method shown in the figure below and check Open generated HTML in browser

Export results:

3.3.7 JUnit plug-in automatically generates single test code

(1) Install the plug-in, restart the idea to take effect

(2) Configure plug-ins

(3) Using plug-ins

Right-click generate... on the class that needs to generate unit test code, as shown in the figure below.

Generate result:

4. Unit testing tool - Mockito

4.1 Introduction to Mockito

In the process of unit testing, it is advocated not to rely on specific interfaces and data sources. At this time, it involves the simulation of related data, such as the return results of Http and JDBC, etc., which can be simulated by using virtual objects, namely Mock objects, so that unit tests are not coupled. .

Prerequisites for using the Mock process:

(1) It is difficult to construct the actual object

(2) The specific behavior of the actual object is difficult to be triggered

(3) The actual object may not exist yet, for example, the dependent interface has not been developed yet, etc.

Mockito official website : https://site.mockito.org  . Like JUnit, Mockito is a mock data frame specifically for the Java language. It has very similar functions to the similar EasyMock and jMock, but the tool is simpler and easier to use.

Features of Mockito:

(1) You can mock classes not just interfaces

(2) Simple and easy to understand through annotations

(3) Support sequential verification

(4) With parameter matcher

4.2 Use of Mockito

The introduction of spring-boot-starter-test by maven will automatically introduce mockito into the project.

4.2.1 Use cases

(1) In the previous code, a BookService interface is defined, which means the interface of borrowing books, and it will not be implemented for the time being

public interface BookService {
    Book orderBook(String name);
}

(2) Add an orderBook method in the previous StudentService class, which means the method for students to order books, and the implementation content calls the above-mentioned orderBook method of BookService.

public Book orderBook(String name) {
   return bookService.orderBook(name);
}

(3) Write a unit test method to test the orderBook method of StudentService

@Test
public void orderBookTest() {
    Book expectBook = new Book(1L, "钢铁是怎样炼成的", "书架A01");
    Mockito.when(bookService.orderBook(any(String.class))).thenReturn(expectBook);
    Book book = studentService.orderBook("");
    System.out.println(book);
    Assert.assertTrue("预定书籍不符", expectBook.equals(book));
}

(4) Execution result:

(5) Result analysis

The above content does not implement the orderBook (String name) method of the BookService interface. However, after using mockito to simulate the data, it passed the unit test. The reason is that Mockito replaced the object that was originally obtained in the orderBook method of StudentService. Here, it is simulated that the object is difficult to obtain or cannot be obtained currently. data instead.

4.2.2 Related syntax

Commonly used APIs:

In the above case, syntaxes such as when, any, and theWhen of mockito are used. Next, what are the commonly used APIs:

1) mock: Simulate a required object

2) when: generally used together with thenXXX to indicate what happens after an operation is performed.

3) any: Return the default value of a specific object. In the above example, any data of String type can be filled in.

4) theReturn: returns the specified result after performing a specific operation.

5) spy: Create a monitoring object.

6) verify: Verify specific behavior.

7) doReturn: return the result.

8) doThrow: Throw a specific exception.

9) doAnswer: Make a custom response.

10) times: the number of times the operation is executed.

11) atLeastOnce: The operation must be performed at least once.

12) atLeast: The operation is performed at least the specified number of times.

13) atMost: The operation is executed up to the specified number of times.

14) atMostOnce: The operation is executed at most once.

15) doNothing: Do nothing.

16) doReturn: returns a result.

17) doThrow: Throw a specified exception.

18) doAnswer: Specify a specific operation.

19) doCallRealMethod: used to monitor the object to return a real result.

4.2.3 Use points

(1) Piling

There is Stub in Mockito, the so-called stub or the concept of piling. In the above case, Mockito.when (bookService.orderBook (any (String.class))).thenReturn (expectBook); is the meaning of piling. First define it. If you follow the established What is called in the way, the result is what is output. Then use Book book = studentService.orderBook (""); to output the specified result according to the specified stub.

@Test
    public void verifyTest() {
        List mockedList = mock(List.class);

        mockedList.add("one");

        verify(mockedList).add("one");			// 验证通过,因为前面定义了这个桩
        verify(mockedList).add("two");			// 验证失败,因为前面没有定义了这个桩
    }

(2) Parameter matching

The any (String.class) in the orderBook method of StudentService in the above example is the parameter matcher, which can match any data of String type defined here.

(3) Times verification

@Test
    public void timesTest() {
        List mockedList = mock(List.class);
        when(mockedList.get(anyInt())).thenReturn(1000);
        System.out.println(mockedList.get(1));
        System.out.println(mockedList.get(1));
        System.out.println(mockedList.get(1));
        System.out.println(mockedList.get(2));

        // 验证通过:get(1)被调用3次
        verify(mockedList, times(3)).get(1);
        // 验证通过:get(1)至少被调用1次
        verify(mockedList, atLeastOnce()).get(1);
        // 验证通过:get(1)至少被调用3次
        verify(mockedList, atLeast(3)).get(1);
    }

(4) Sequential verification

@Test
    public void orderBookTest1() {
	    String json = "{"id":12,"location":"书架A12","name":"三国演义"}";
	    String json1 = "{"id":21,"location":"书架A21","name":"水浒传"}";
        String json2 = "{"id":22,"location":"书架A22","name":"红楼梦"}";
        String json3 = "{"id":23,"location":"书架A23","name":"西游记"}";
        when(bookService.orderBook("")).thenReturn(JSON.parseObject(json, Book.class));
        Book book = bookService.orderBook("");
        Assert.assertTrue("预定书籍有误", "三国演义".equals(book.getName()));

        when(bookService.orderBook("")).thenReturn(JSON.parseObject(json1, Book.class)).
                thenReturn(JSON.parseObject(json2, Book.class)).
                thenReturn(JSON.parseObject(json3, Book.class));
        Book book1 = bookService.orderBook("");
        Book book2 = bookService.orderBook("");
        Book book3 = bookService.orderBook("");
        Book book4 = bookService.orderBook("");
        Book book5 = bookService.orderBook("");
        // 全部验证通过,按顺序最后打桩打了3次,大于3次按照最后对象输出
        Assert.assertTrue("预定书籍有误", "水浒传".equals(book1.getName()));
        Assert.assertTrue("预定书籍有误", "红楼梦".equals(book2.getName()));
        Assert.assertTrue("预定书籍有误", "西游记".equals(book3.getName()));
        Assert.assertTrue("预定书籍有误", "西游记".equals(book4.getName()));
        Assert.assertTrue("预定书籍有误", "西游记".equals(book5.getName()));
}

(5) Exception verification

@Test(expected = RuntimeException.class)
    public void exceptionTest() {
        List mockedList = mock(List.class);
        doThrow(new RuntimeException()).when(mockedList).add(1);
        // 验证通过
        mockedList.add(1);
    }

Finally: The complete software testing video tutorial below has been organized and uploaded, and friends who need it can get it by themselves [Guaranteed 100% free]

insert image description here

Software Testing Interview Documentation

We must study to find a high-paying job. The following interview questions are the latest interview materials from first-tier Internet companies such as Ali, Tencent, and Byte, and some Byte bosses have given authoritative answers. Finish this set The interview materials believe that everyone can find a satisfactory job.

picture

Acquisition of complete set of information

Guess you like

Origin blog.csdn.net/weixin_50829653/article/details/130951996
Recommended