1, Create unit test service
Right click Service - > New - > other - > JUnit test case - > next, select the method to create the unit test, or click finish directly
Relevant dependencies are attached at the end of the article
2, Sample code
// Get dev environment configuration file @ActiveProfiles("dev") // Specify startup class @SpringBootTest(classes = ProjectApplication.class) // If you want to use the Spring test framework function (for example, @ MockBean) in your test, you must use @ ExtendWith(SpringExtension.class). // It replaces the deprecated JUnit4@RunWith(SpringJUnit4ClassRunner.class) @ExtendWith(SpringExtension.class) public class AppApplicationManagerServiceTest { // You need to add @ SpyBean or @ MockBean annotation to return the value of the template method // @SpyBean annotation will call the real method if there is no simulated method return @SpyBean private AppApplicationManagerService appApplicationManagerService; // @The MockBean annotation returns null if no mock method returns @MockBean private CommonService commonService; @BeforeEach // Each test method is executed once before public void setUp() { when(commonService.getOsreIdByCode(Mockito.anyString())).thenReturn(107); } // Final result display name @DisplayName("According to tenant code+Application code to obtain the list of application administrators") // Parametric testing can be used in combination with @ CsvSource, @ EnumSource, @ MethodSource and other annotations @ParameterizedTest // The parameters are separated by commas. Taking this as an example, this test method will be executed three times, and the parameters correspond to each other @CsvSource({ "dev002, SP-CI, hrdashboard", "dev002, CDPLIFE, hrdashboard", "dev002, third, hrdashboard" }) public void testGetApplicationManager(String tenantCode, String appCode, String sysCode) { // If approlemapper. Is called Selectbyexample (any()) method. If the parameter is an object of any type, null will be returned // This method can be used to simulate the return value for database related operations or calling other microservice operations when(appRoleMapper.selectByExample(any())).thenReturn(null); // Execution test method List<String> resList = appApplicationManagerService.getApplicationManager(tenantCode, appCode, sysCode); if ("SP-CI".equals(appCode)) { boolean judge = DataUtils.isEmptyList(resList); // Assertion, each test method assertion is preferably greater than or equal to 1 assertTrue(judge); } } }
3, Annotation
1,@SpyBean,@MockBean,@Autowired
-
If you do not need to simulate the return value of the method, you can use @ Autowired or @ SpyBean
-
If you need to call both real methods and simulated method return values, you can only use @ SpyBean
-
If you only need to simulate the return value of the method, you can use @ MockBean or @ SpyBean
2,@ExtendWith(SpringExtension.class)
If you want to use the Spring test framework function (for example, @ MockBean) in your test, you must use @ ExtendWith(SpringExtension.class). It replaces the deprecated JUnit4@RunWith(SpringJUnit4ClassRunner.class)
3,@BeforeEach,@BeforeAll,@AfterAll,@AfterEach
-
@Before each: each test method is executed before execution
-
@After each: each test method will be executed at the end of execution
-
@Before all: all test methods will be executed before execution
-
@After all test methods are executed
4,@DisplayName()
The name of the method displayed in the run result
5,@ParameterizedTest,@RepeatedTest()
For parametric testing, you need to replace @ Test and add at least one source. The source will provide parameters for each call, and then use the parameters in the Test method. as
@ParameterizedTest @CsvSource({ "dev002, SP-CI, hrdashboard", "dev002, CDPLIFE, hrdashboard", "dev002, third, hrdashboard" })
This means that the method with these two annotations will be executed three times, and three parameters will be passed in each time
@RepeatedTest(n): repeatability test, i.e. n times
6,@ValueSource,@EnumSource,@MethodSource,@CsvSource,@CsvFileSource,@ArgumentsSource
These annotations are used in combination with @ ParameterizedTest to specify the parameter source.
// Execute the method three times. The type also supports float, String, long and other types @ValueSource(ints = { 1, 2, 3 })
// @EnumSource provides a convenient way to use Enum constants. This comment provides an optional name parameter that allows you to specify which constant should be used. If omitted, all constants will be used, as shown in the following example. @EnumSource(TimeUnit.class)
// @MethodSource allows you to reference one or more methods of a test class or an external class. Such a method must return a stream, iteratable, iterator, or an array of parameters. In addition, this method cannot accept any parameters. The factory method in the test class must be static, unless the test class is annotated with @ TestInstance(Lifecycle.PER_CLASS); @MethodSource("roleAndMenuParam") void testSavePlatformRoleAndAuthMenu(PlatformAdminAuthMenuForm platformAdminAuthMenuForm) { } static List<PlatformAdminAuthMenuForm> roleAndMenuParam() { }
// Parameters are separated by commas. Taking this as an example, the test method will be executed three times and three parameters will be passed in @CsvSource({ "dev002, SP-CI, hrdashboard", "dev002, CDPLIFE, hrdashboard", "dev002, third, hrdashboard" })
// @CsvFileSource allows the use of CSV files in the classpath. Each line in the CSV file invokes a parametric test. @CsvFileSource(resources = "/two-column.csv", numLinesToSkip = 1)
@ArgumentsSource can be used to specify a custom and reusable ArgumentsProvider.
7,@Disabled
The method with this annotation will not be executed when running
4, Assert
Assertions suggest at least one in each test method
Junit5's assertions support Lambda expressions
Some common assertions are listed below:
-
Assert equals: asserts that the expected and actual are equal
-
assertNotNull: the result is not null
-
assertThrows: verify the exception. If no exception is thrown or the exception thrown is inconsistent with the expected exception, the assertion will fail
-
assertAll: grouping assertions, which can be composed of multiple assertions. As long as one of them fails, it will not continue to be executed
More assertions are not listed
5, Simulation method return value
When writing a unit test, you sometimes need to let the method return the result you want. In this case, you need to use mock to simulate the return value of the method
The principle of mockito simulation is to create an anonymous subclass that inherits the original class through reflection, and use the created subclass to execute instead of the original class. Because it is inheritance, static methods, final methods and private methods cannot be overridden through subclasses
Analog return value
// Original method appRoleMapper.selectByExample(AppRoleExample example); // Analog return value when(appRoleMapper.selectByExample(any())).thenReturn(null);
The function of this method is to execute approlemapper Selectbyexample (approleexample example) method. If the parameter is of any type, it will not call the database and will directly return null
doNothing
Some methods have no return value, such as sending messages. If you don't need to call these methods, you can use doNothing method, so you won't execute this method when you encounter this method. An example is as follows:
// Original method commonService.verifyFieldRequired(Object obj, String jsonPaths){} // doNothing using Mockito.doNothing().when(commonService).verifyFieldRequired(Mockito.any(), Mockito.anyString());
Mockito parameter
When simulating a method, you need parameters. You can specify any() or anyString() to specify the parameter type. Any represents any type of object, anyString() represents any String type parameter, and other types:
any(Class type): specific classes can be passed in. If any(AppRole.class) represents any role object
anyBoolean(): data of any boolean type
anyListOf(Class clazz): represents a list of a type
anyObject(): any object
More methods are not listed one by one
Create simulation objects
Test data sometimes need more, and it is cumbersome to create one by one. You can use EasyRandom to create random objects
EasyRandom easyRandom = new EasyRandom(); // Generate a single object AppMenu menu = easyRandom.nextObject(AppMenu.class); // Generate list List<AppMenu> menuList = easyRandom.objects(AppMenu.class, 10).collect(Collectors.toList()); // Generate other types of data boolean boo = easyRandom.nextBoolean();
If no parameters are set, the randomly generated data is chaotic. You can add a configuration to make the generated data more meet our requirements. The use method is as follows
EasyRandomParameters param = new EasyRandomParameters(); // Set string length range param.setStringLengthRange(new Range(5,10)); // When creating an object, the configuration parameters are passed in EasyRandom easyRandom = new EasyRandom(param); for (int i = 0; i < 5; i++) { String str = easyRandom.nextObject(String.class); System.out.println(str); } // result tniOAot nzWvk QtWruzGVy IdgFUY NdkFYkk
See the official documentation for more configurations: https://github.com/j-easy/easy-random/wiki/Randomization-parameters
Simulate the return value of static method and private method
As mentioned earlier, mockito is simulated by creating subclasses of the original class through reflection. This method cannot simulate static methods, private methods and final methods. At this time, JMockit can be used to implement it
JMockit Chinese website: http://jmockit.cn/index.htm
JMock usage method 1:
class LogTestServiceTest { @Autowired private static LogTestService logTestService = new LogTestService(); @Test void testTest() throws Exception { new Expectations(DataUtils.class, LogTestService.class) { { // mock static method DataUtils.isEmptyList(Mockito.anyList()); result = false; // mock final method logTestService.finalTest(Mockito.anyString()); result = "mock final"; // Private methods and native methods cannot be mock in this way } }; logTestService.test(); } }
JMock application method 2:
class LogTestServiceTest { @Autowired private static LogTestService logTestService = new LogTestService(); // Write an inner class to inherit MockUp public static class LogTestServiceTestMockUp extends MockUp<LogTestService> { @Mock private String privateTest(String str) { return "mock private method"; } @Mock final String finalTest(String str) { return "mock final method"; } } public static class DataUtilsMockUp extends MockUp<DataUtils> { @Mock public static boolean isEmptyList(List list) { return false; } } @Test void testTest() throws Exception { new LogTestServiceTestMockUp(); new DataUtilsMockUp(); logTestService.test(); } }
6, Project dependency
<!-- mockito --> <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-core</artifactId> <version>3.11.2</version> <scope>test</scope> </dependency> <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-all</artifactId> <version>1.10.19</version> <scope>test</scope> </dependency> <!-- junit5 --> <dependency> <groupId>org.junit.platform</groupId> <artifactId>junit-platform-commons</artifactId> <version>1.8.2</version><!--$NO-MVN-MAN-VER$--> </dependency> <dependency> <groupId>org.junit.platform</groupId> <artifactId>junit-platform-launcher</artifactId> <version>1.8.2</version><!--$NO-MVN-MAN-VER$--> <scope>test</scope> <exclusions> <exclusion> <groupId>org.junit.platform</groupId> <artifactId>junit-platform-commons</artifactId> </exclusion> </exclusions> </dependency> <!-- JMock --> <dependency> <groupId>org.jmockit</groupId> <artifactId>jmockit</artifactId> <version>1.36</version> <scope>test</scope> </dependency>