Introduce maven dependency
powermock is introduced to solve the problem of static method mock.
<dependency> <groupId>org.powermock</groupId> <artifactId>powermock-module-junit4</artifactId> <version>2.0.2</version> <scope>test</scope> </dependency> <dependency> <groupId>org.powermock</groupId> <artifactId>powermock-api-mockito2</artifactId> <version>2.0.2</version> <scope>test</scope> </dependency> <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-core</artifactId> <version>2.28.2</version> <scope>test</scope> </dependency> <dependency> <groupId>org.assertj</groupId> <artifactId>assertj-core</artifactId> <version>3.11.1</version> <scope>test</scope> </dependency> <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-all</artifactId> <version>2.0.2-beta</version> <scope>test</scope> </dependency>
Building Unit Test Directory
Like the standard maven unit test directory, add application.yml to the resources directory as follows:
spring: profiles: active: test --- spring: profiles: test dc: security: auditlog: module: "System Manage" #Module name logging: level: root: INFO
Abstract test parent class
@RunWith(PowerMockRunner.class) @PowerMockRunnerDelegate(SpringRunner.class) @PowerMockIgnore({"javax.management.*", "javax.net.ssl.*"}) @SpringBootTest @ActiveProfiles("test") public abstract class AbstractSpringTest { @MockBean protected ISecurityContextService securityContextService; @SpyBean protected RestTemplate restTemplate; @Autowired protected AuditLogProperties properties; }
Actual unit test case
@PrepareForTest({SecurityContextHolder.class, ClientContextHolder.class}) public class ApiOperationLogServiceTest extends AbstractSpringTest { private ApiOperationLogService operationLogService; @Before public void setUp() throws Exception { System.out.println("###################test start##########################"); operationLogService = new ApiOperationLogService(properties, restTemplate); } @After public void tearDown() throws Exception { System.out.println("###################test end##########################"); } @Test public void save() { ResultVo resultVo = new ResultVo(); resultVo.setResultCode(0); resultVo.setResultMessage("save log success"); ResponseEntity<ResultVo> response = new ResponseEntity<>(resultVo, HttpStatus.OK); doReturn(response).when(restTemplate).exchange(eq(properties.getOperationLogUrl()), eq(HttpMethod.POST), isA(HttpEntity.class), eq(ResultVo.class)); operationLogService.save(new OperationLog()); } @Test public void testLoadOperatorId() { // PowerMockito Pile Driving, Static Simulation Method PowerMockito.mockStatic(SecurityContextHolder.class); SecurityContextImpl securityContext = new SecurityContextImpl(); securityContext.setAuthentication( new UsernamePasswordAuthenticationToken("xiongneng", "123456")); PowerMockito.when(SecurityContextHolder.getContext()).thenReturn(securityContext); assertEquals(operationLogService.loadOperatorId(), "xiongneng"); } @Test public void testLoadClientIpWhenRemoteUserIp() { // PowerMockito pile driving, simulated static method PowerMockito.mockStatic(ClientContextHolder.class); ClientContext clientContext = new ClientContext(); clientContext.setClientIP("192.168.20.22"); clientContext.setRemoteUserIP("30.200.12.22"); PowerMockito.when(ClientContextHolder.getContext()).thenReturn(clientContext); assertEquals(operationLogService.loadClientIp(), "30.200.12.22"); } @Test public void testLoadClientIpWhenNoRemoteUserIp() { // PowerMockito pile driving, simulated static method PowerMockito.mockStatic(ClientContextHolder.class); ClientContext clientContext = new ClientContext(); clientContext.setClientIP("192.168.20.22"); PowerMockito.when(ClientContextHolder.getContext()).thenReturn(clientContext); assertEquals(operationLogService.loadClientIp(), "192.168.20.22"); } }
Differences between MockBean and SpyBean
There are two differences between spy object and mock object:
1. The difference of default behavior
For the method without specified mock, spy will call the real method by default. For the method with return value, it will return the real return value. For the method with return value, it will not execute by default. For the method with return value, it will return null by default.
2. Different ways of using mock
The use of mock objects, spy objects use this method directly, so it can not be used in this way, such as:
Mockito.when(obj.domethod(parm1, param2)).thenReturn(result);
For the usage of spy objects, do and other methods should be executed first. mock objects can also be used in this way, such as:
Mockito.doReturn(info).when(obj).domethod(param1, param2);
@ The difference between Spy and @SpyBean, @Mock and @MockBean
- Objects generated by spy and mock are not managed by spring
- When the spy calls the real method, other beans cannot be injected. To use the injection, use the spy bean.
- The objects generated by SpyBean and MockBean are managed by spring, which is equivalent to automatically replacing the injection of corresponding type beans, such as @ Autowired.
Simulation void method
There are two ways to simulate the void method, one is to throw an exception, the other is to specify the execution process of void through Answer.
Throw the expected exception:
doThrow(RuntimeException.class).when(daoMock).updateEmail(any(Customer.class), any(String.class));
Specify the void execution process:
doAnswer((Answer<Void>) invocation -> { Object[] args = invocation.getArguments(); System.out.println("restTemplate.exchange called with arguments: " + Arrays.toString(args)); return null; }).when(restTemplate).exchange(anyString(), eq(HttpMethod.POST), isA(HttpEntity.class), eq(ResultVo.class)); // Implement real methods doAnswer(Answers.CALLS_REAL_METHODS.get()).when(mock).voidMethod(any(SomeParamClass.class));