评论

收藏

[Java] 如何使用Mockito调用静态方法和void方法

编程语言 编程语言 发布于:2021-10-08 21:46 | 阅读数:360 | 评论:0

这篇文章主要介绍了如何使用Mockito调用静态方法和void方法的操作,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
一、mock 静态方法
mockito库并不能mock静态方法,需要依赖powermock
第一步:给类添加注解
// 静态类优先加载,所以需要提前告诉powermock哪些静态类需要mock
@ContextConfiguration
@RunWith(PowerMockRunner.class)
@PowerMockRunnerDelegate(SpringJUnit4ClassRunner.class)
@PrepareForTest(静态调用类.class)
public class SupplierServiceImplTest extends PowerMockTestCase {}
第二步:mock使用
@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 方法
// void嘛,doNothing顾名思义
PowerMockito.doNothing().when(casService).addSupplier(anyLong(), any(ServiceKey.class));
使用PowerMockito和Mockito进行模拟测试
包括静态方法测试,私有方法测试等,以及方法执行的坑或者模拟不成功解决
一 普通spring项目
依赖:这个很重要,不同版本用法也有点区别:
<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对象
order = mock(Order.class);
user = mock(User.class);
二 属性注入
将service等类中需要的其他service或者mapper等mock出来,然后分别使用工具类注入,名称保持一致即可
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
//account 获取stub
PowerMockito.mockStatic(Account.class);
Mockito.when(Account.get(anyString(), anyString(), anyString(), anyInt())).thenReturn(account);
四 私有方法
私有方法首先需要在类上加入注解,对于要测试的类中的public方法同样有效,比如测试方法中包含一个public方法,可以同样模拟:
@PrepareForTest(ConsumptionServiceImpl.class)  //里面写需要模拟私有方法的类class
然后对象不能mock,必须new一个,并且需要用spy处理:
orderService = PowerMockito.spy(new OrderServiceImpl());
接着使用doreturn .when这种形式模式,不能使用先when后return这种,会报错
注意一点,模拟参数要么全部模拟,要么全部自定义,不能混搭
这里有个大坑,如果出现私有方法还是进去执行的情况,很大可能是参数不对,比如你mock的参数是 anyString(),那么你真是测试时候传递的必须是一个String实例,不能是null,否则mock就会失败,我这里之前一直是对象的一个属性,直接new了一个对象传递
所以一直不成功:
比如 方法需要的是user.getId() ,而且你mock的是一个anyInt(),那么真正传递的时候必须给这个user,setId(9527),否则就无法达成预期的模拟效果,所有方法都一样!!
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 依赖
<!-- 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 创建测试基类
/**
 * 测试基类,所有子测试类继承此类即可
 */
@PowerMockRunnerDelegate(SpringRunner.class)
@RunWith(PowerMockRunner.class)
@PowerMockIgnore({"javax.management.*", "javax.security.*"}) //忽略一些mock异常
@SpringBootTest
public class TestBase {
}
3 创建特定的测试类
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 模拟私有方法和静态方法
@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模块的启动类的启动注解给注释掉,否则测试启动会报错
以上为个人经验,希望能给大家一个参考,也希望大家多多支持CodeAE代码之家
原文链接:https://blog.csdn.net/csdn9988680/article/details/80807736

关注下面的标签,发现更多相似文章