Small knowledge, big challenge! This paper is participating in theEssentials for programmers”Creative activities

I remember when I was interviewed, the interviewer asked me whether I would test during the normal development process. I replied, “Of course, I can write my own code.” Now I think I misunderstood him. He probably wanted to ask me about unit testing, integration testing and related knowledge behind it, but when it came to testing, I only knew Junit. So today I’ll talk about the tests involved in the development process and the associated technology stack. While testing can be divided into unit testing, integration testing, system testing, etc., as development, we may not need to do so much testing (and sometimes not even…). Unit testing and integration testing are closely related to development. Unit tests are module tests. I understand that a module is a class, mainly referring to our Service module, because most of the business logic in a project is in the Service layer. Let’s just focus on testing the methods in Service. Integration test is the joint test of multiple modules, the comprehensive test of Service and Dao among multiple modules. That said, how do we test? We often use is the Junit framework, saying to the test, I haven’t been clear before, only now a little clue, don’t know if you will encounter this problem, if find methods involved in the test was fine, but involves the modify data operation, our test case can’t repeat, because complains, This is really embarrassing, partly because the data is not correct, partly because the database produces a lot of junk data. How can this be avoided? If the test item is relatively simple (the number of tables is not large), we can configure a test profile and configure the H2 in-memory database in the file, so that the normal database will not be affected, but the H2 related configuration is still boring, and we need to build our own table, insert related data. How can we ensure that the test case can be repeated in unit tests without relying on a database? Mock: Mock: Mock: Mock: Mock: Mock: Mock: Mock: Mock: Mock: Mock: Mock: Mock: Mock: Mock: Mock: Mock: Mock: Mock You can also verify that the business logic is correct (my preconceived notion here is that unit testing is primarily about testing the various business logic at the Service layer). The best framework for Mock pattern testing in Java is Mockito. There are other Mock pattern testing frameworks, but they are not on our list. I use the Mockito framework in my own projects and was not used to Mock everything, but now it feels, well, delicious! So I need to share it with you. The advantages of Mockito are that it combines well with Junit, is extremely easy to use, the test code is simple, very readable (you can feel it when you are familiar with writing it), and it is the most popular Java testing framework. When writing test cases using Mockito, make sure you know what needs to be fake and what needs to be true, otherwise you will go crazy and feel like it is all fake. Here is how to quick start it! Here I use Spring Boot2, Junit5, Mockito3, IDEA2019.3.4, Maven combination form, everything is fresh, the last will give a Demo project, you can also take to practice oh. Let’s take a quick look at the example, which I wrote deliberately, to indicate that the business logic is so complex that at least 3 cases are required to cover the method 100%. Username is not empty, username is” Kris”, and username is empty. Methods to be tested:

@Service public class UserServiceImpl implements UserService{ @Autowired private UserDao userDao; @Override public User save(User user) { if(! user.getUsername().isEmpty()) { if(user.getUsername().equals("Kris")){ user.setAge(18); return userDao.save(user); } return userDao.save(user); } else { return null; }}}Copy the code

The standard test case should be as follows. These three test cases cover the save method 100%. We can use Run with coverage to execute the test module and see the coverage.

@ExtendWith(MockitoExtension.class) class UserServiceTest { @InjectMocks UserServiceImpl userService; @Mock UserDao userDao; @Test void save() { Mockito.when(userDao.save(Mockito.any())).thenReturn(new User("test",12)); User user = userService.save(new User("test",12)); assertEquals("test",user.getUsername()); } @Test void save1() { Mockito.when(userDao.save(Mockito.any())).thenReturn(new User("Kris",12)); User user = userService.save(new User("Kris",3)); assertEquals(12,user.getAge()); } @Test void save3() { User user = userService.save(new User("",12)); assertNull(user); }}Copy the code

Here’s why and what you might encounter! First of all, I encountered a big problem. I used IDEA locally, but I couldn’t run the test case. The reason was that THE jar package of Junit5 was automatically introduced in IDEA2016.2, but this automatic introduction dependency was not done well. People Junit5 has been upgrading, you need to add to the automatic introduction! You don’t have to. The local version of IDEA I use is 2017.1, and the reference document given by Junit5 says it is better to use 2017.3 and later versions. After a long time of work, I still cannot run the test case locally. I am so annoyed that I have to upgrade my IDEA. Now the latest version is 2019.3.4, no problem. Here is the code level pit: First, don’t forget to add @extendwith (mockitoExtension.class) to Junit5 to add Mock execution environments, initialize Mock instances, and strictly bind stubbings (more on this later). Second, all the methods in the UserService interface that we are testing here, note that the implementation class UserServiceImpl is injected when injecting the object under test, since this is the module we are testing. If you have dependencies on other services, you can use the @mock interface directly. Third, if the class you are testing depends on other objects, you need to use @Mock to introduce related modules, but the class you are testing uses the @InjectMocks annotation, as shown in UserServiceImpl. Fourth, the shape

Mockito.when(userDao.save(Mockito.any())).thenReturn(new User("test",12));
Copy the code

Userdao.save (User User) is set up in advance to cheat. This is similar to how I saved the stub. You will encounter it anyway, and Mockito3 requires that, Write stub must be used to, or it will quote UnnecessaryStubbingException anomaly, often appear this exception, so a stub not one more, not one less! Note that the save3 caes does not have a stub because it cannot be written. Fifth, the stub defines a specific return value, so the final return value of the virtual method is defined by thenReturn(), regardless of the parameter passed in. Any or similar mockito.anylong () must be used as arguments to Mock methods in when(). Since the method is false, there is no need to give true parameters, give an error, false to do a full set of it. Sixth, the last is the test method of parameter if need, must not be involved in Mockito. Any such false parameter, literally what you write, look at not just null pointer exception, because is the method of your test, you also use virtual parameters, you really are false home, here must want to understand, you exactly is which method of testing, Which method do you want to Mock? I suggest you look at it together with # 5. In Mockito3, stubs are strictly stubs that do not allow you to write too much. Strictness = strictness.LENIENT you can use @mockitoSettings (strictness = strictness.LENIENT) on your method or class to be flexible with stubs. I would recommend not changing the default values and keeping the code simple. By the way, in case you don’t understand, Mock is a kind of testing mode again. When we say Mock mode testing, we mean how to fake in the test but can really test the module we want to test. Mockito is an implementation of Mock mode, which is a technical framework for testing. A common exception in testing is null Pointers, which can be easier to spot if there is no @mock dependent object, or if there is no assignment to the method argument under test, or if the return value defined in thanReturn() lacks an attribute. A perfect test case should be one line of code, write no more, assign no more, and add whatever you need. The points summarized above are my best practices when writing test cases. I hope they can help you. If you have any other questions, please send me a private message and study together.