一、mock 静态方法
mockito库并不能mock静态方法,需要依赖powermock
第一步:给类添加注解
1
2
3
4
5
6
|
// 静态类优先加载,所以需要提前告诉powermock哪些静态类需要mock @ContextConfiguration @RunWith (PowerMockRunner. class ) @PowerMockRunnerDelegate (SpringJUnit4ClassRunner. class ) @PrepareForTest (静态调用类. class ) public class SupplierServiceImplTest extends PowerMockTestCase {} |
第二步:mock使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
@Test (expectedExceptions = BusinessException. class ) public void testAddSupplierAccount_genIdentityNoError() { // 告诉powermock,需要mock该类的所有静态方法 PowerMockito.mockStatic(PasswordGenerator. class ); final SupplierAccountDto supplierAccountDto = new SupplierAccountDto(); supplierAccountDto.setName( "小明" ); final String randomPWd = "666" ; PowerMockito.when(supplierDao.selectByEmail(anyString())) .thenReturn( new ArrayList<HaitaoSupplier>()); // 静态方法mock PowerMockito.when(PasswordGenerator.genPwd()).thenReturn(randomPWd); PowerMockito.when(pwEncoder.encode(anyString())).thenReturn(randomPWd); PowerMockito.when(identityNoGenerator.genIdentityNo()).thenReturn(-1L); supplierServiceImpl.addSupplierAccount(supplierAccountDto); verify(pwEncoder).encode(randomPWd); } |
二、mock void 方法
1
2
|
// void嘛,doNothing顾名思义 PowerMockito.doNothing().when(casService).addSupplier(anyLong(), any(ServiceKey. class )); |
使用PowerMockito和Mockito进行模拟测试
包括静态方法测试,私有方法测试等,以及方法执行的坑或者模拟不成功解决
一 普通spring项目
依赖:这个很重要,不同版本用法也有点区别:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
< dependency > < groupId >org.mockito</ groupId > < artifactId >mockito-all</ artifactId > < version >2.0.2-beta</ version > < scope >test</ scope > </ dependency > < dependency > < groupId >org.powermock</ groupId > < artifactId >powermock-api-mockito</ artifactId > < version >1.7.4</ version > < scope >test</ scope > </ dependency > < dependency > < groupId >org.powermock</ groupId > < artifactId >powermock-module-junit4</ artifactId > < version >2.0.0</ version > < scope >test</ scope > </ dependency > < dependency > < groupId >org.powermock</ groupId > < artifactId >powermock-core</ artifactId > < version >1.7.4</ version > < scope >test</ scope > </ dependency > |
接下来就是mock测试了,使用完全模拟测试过程,对于需要测试接口中调用的静态,私有方法等,返回自己想要的预期结果,达到测试效果:
这里有几个要点:
测试过程中完全手动mock,不会真实调用或者产生数据
一 mock对象
1
2
|
order = mock(Order. class ); user = mock(User. class ); |
二 属性注入
将service等类中需要的其他service或者mapper等mock出来,然后分别使用工具类注入,名称保持一致即可
1
2
3
4
5
6
7
|
roomTypeService = mock(RoomTypeServiceImpl. class ); ticketComponetService = mock(TicketComponetServiceImpl. class ); hotelMapper = mock(HotelMapper. class ); //注入属性 ReflectionTestUtils.setField(orderService, "hotelGroupMapper" , hotelGroupMapper); ReflectionTestUtils.setField(orderService, "dsUtils" , dsUtils); ReflectionTestUtils.setField(orderService, "orderMapper" , orderMapper); |
三 静态方法mock
模拟静态方法返回结果需要使用PowerMockit,测试类上必须加注解@PrepareForTest
1
2
3
|
//account 获取stub PowerMockito.mockStatic(Account. class ); Mockito.when(Account.get(anyString(), anyString(), anyString(), anyInt())).thenReturn(account); |
四 私有方法
私有方法首先需要在类上加入注解,对于要测试的类中的public方法同样有效,比如测试方法中包含一个public方法,可以同样模拟:
1
|
@PrepareForTest (ConsumptionServiceImpl. class ) //里面写需要模拟私有方法的类class |
然后对象不能mock,必须new一个,并且需要用spy处理:
1
|
orderService = PowerMockito.spy( new OrderServiceImpl()); |
接着使用doreturn .when这种形式模式,不能使用先when后return这种,会报错
注意一点,模拟参数要么全部模拟,要么全部自定义,不能混搭
这里有个大坑,如果出现私有方法还是进去执行的情况,很大可能是参数不对,比如你mock的参数是 anyString(),那么你真是测试时候传递的必须是一个String实例,不能是null,否则mock就会失败,我这里之前一直是对象的一个属性,直接new了一个对象传递
所以一直不成功:
比如 方法需要的是user.getId() ,而且你mock的是一个anyInt(),那么真正传递的时候必须给这个user,setId(9527),否则就无法达成预期的模拟效果,所有方法都一样!!
1
2
3
4
5
6
7
8
|
try {<br> // 方法名,方法参数,必须全部对应,否则报错方法找不到 PowerMockito.doReturn( 1 ).when(orderService, "dateListMinBook" ,anyString(),anyString(),any(RoomType. class ),anyString(),anyString()); PowerMockito.doReturn(ResponseMessage.success().pushData( "dateRoomTypeList" , new ArrayList<DateRoomType>())).when(orderService, "eachDateNumAndPrice" ,any(Order. class ),any(RoomType. class ),anyBoolean(),anyInt(),anyString(),any(User. class )); PowerMockito.doReturn( "2000" ).when(orderService, "getKeeptimeByWxcidAndHotelidAndLevel" ,anyString(),anyString(),anyString()); PowerMockito.doNothing().when(orderService, "getPayWay" ,any(),any(),any(),any(),any()); } catch (Exception e) { e.printStackTrace(); } |
五 预期结果
verify :判断方法执行了几次: 确定测试是否通过
例如:verify(userService, times(1)).queryUser(any(anyInt(),anyString(),anyString());
二 springboot项目使用
1 依赖
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
|
<!-- S-junit --> < dependency > < groupId >org.mockito</ groupId > < artifactId >mockito-all</ artifactId > < version >2.0.2-beta</ version > < scope >test</ scope > </ dependency > < dependency > < groupId >org.powermock</ groupId > < artifactId >powermock-api-mockito2</ artifactId > < version >2.0.0-beta.5</ version > < scope >test</ scope > </ dependency > < dependency > < groupId >org.powermock</ groupId > < artifactId >powermock-module-junit4</ artifactId > < version >2.0.0-beta.5</ version > < scope >test</ scope > </ dependency > < dependency > < groupId >org.powermock</ groupId > < artifactId >powermock-core</ artifactId > < version >2.0.0-RC.4</ version > < scope >test</ scope > </ dependency > < dependency > < groupId >cglib</ groupId > < artifactId >cglib</ artifactId > < version >3.2.9</ version > </ dependency > < dependency > < groupId >org.mockito</ groupId > < artifactId >mockito-inline</ artifactId > < version >2.15.0</ version > </ dependency > < dependency > < groupId >org.assertj</ groupId > < artifactId >assertj-core</ artifactId > < version >3.12.2</ version > < scope >test</ scope > </ dependency > < dependency > < groupId >org.easymock</ groupId > < artifactId >easymock</ artifactId > < version >4.0.2</ version > < scope >test</ scope > </ dependency > <!-- E-junit --> |
2 创建测试基类
1
2
3
4
5
6
7
8
9
|
/** * 测试基类,所有子测试类继承此类即可 */ @PowerMockRunnerDelegate (SpringRunner. class ) @RunWith (PowerMockRunner. class ) @PowerMockIgnore ({ "javax.management.*" , "javax.security.*" }) //忽略一些mock异常 @SpringBootTest public class TestBase { } |
3 创建特定的测试类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
|
public class HotelControllerTest extends TestBase { //继承基类即可 @Mock private HotelService hotelService; private Integer id; // 加载springContext进行mock测试,真实调用方法,不需要mock步骤 // @Autowired // private HotelController hotelController; // 纯mock测试,不加载springContext,执行mock操作,必须mock步骤,不会真实调用 @InjectMocks private HotelController hotelController= new HotelController(); // 应用到所有门店测试 @Test public void detailTest(){ System.out.println( "test start....." ); // 1 构造参数 ininParams( 1 ); // 2 mock步骤 mockStep(); // 3 执行操作 ResponseMessage result = hotelController.detail(id); System.out.println( new Gson().toJson(result)); assertEquals( 0 , ( int ) result.getCode()); } private void mockStep() { when(hotelService.detail(anyInt())).thenReturn(ResponseMessage.success()); } private void ininParams(Integer type) { switch (type){ case 1 : id= 17317 ; break ; case 2 : id= 2 ; break ; default : break ; } } } |
4 模拟私有方法和静态方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
|
@PrepareForTest (OrderServiceImpl. class ) // 需要调用私有或者静态方法的类 public class OrderControllerTest extends TestBase { private OrderServiceImpl orderServiceImpl; //需要调用私有或者静态方法时,不能使用@Mock,还需要@before初始化属性 @Mock private OrderMapper orderMapper; @Mock private RestTemplateUtil restTemplateUtil; private Integer orderId; private String wxcid; @Before public void init(){ //处理私有方法模拟实例 orderServiceImpl = PowerMockito.spy( new OrderServiceImpl()); //使用spy模拟的需要手动注入属性,因为什么都没有 ReflectionTestUtils.setField(orderController, "iOrderService" , orderServiceImpl); ReflectionTestUtils.setField(orderServiceImpl, "orderMapper" , orderMapper); ReflectionTestUtils.setField(orderServiceImpl, "restTemplateUtil" , restTemplateUtil); } //纯mock测试,不加载springContext,执行mock操作,必须mock步骤,不会真实调用 @InjectMocks private OrderController orderController= new OrderController(); @Test public void cancelTest(){ System.out.println( "test start....." ); // 1 构造参数 ininParams(); // 2 mock步骤 mockStep(); // 3 执行操作 ResponseMessage cancel = orderController.cancel(wxcid, orderId); assertEquals( 0 ,( int )cancel.getCode()); } private void mockStep() { Order order = new Order(); order.setStatus( 2 ); when(orderMapper.getOrderByOrderId(anyInt())).thenReturn(order); when(orderMapper.updateStatus(anyInt(),anyInt())).thenReturn( 2 ); JsonObject jsonObject = new JsonObject(); jsonObject.addProperty( "code" , 0 ); when(restTemplateUtil.postToCri(anyString(),anyString(),any())).thenReturn(jsonObject); //处理私有方法,必须用这种写法 try { PowerMockito.doNothing().when(orderServiceImpl, "returnTicketTouser" , anyString(),any()); PowerMockito.doReturn(ErrorCode.SUCCESS).when(orderServiceImpl, "refoundAndGetCode" , any(),any(),any(),any()); } catch (Exception e) { e.printStackTrace(); } } private void ininParams() { wxcid= "57af462dff475fe4644de32f08406aa8" ; orderId= 25864 ; } } |
注意:
如果是分模块项目,springboot项目的启动类只能有一个,即需要把其他service,dao,common模块的启动类的启动注解给注释掉,否则测试启动会报错
以上为个人经验,希望能给大家一个参考,也希望大家多多支持服务器之家。
原文链接:https://blog.csdn.net/csdn9988680/article/details/80807736