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

 更新时间:2021年7月12日 15:00  点击:1829

一、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模块的启动类的启动注解给注释掉,否则测试启动会报错

以上为个人经验,希望能给大家一个参考,也希望大家多多支持猪先飞。

[!--infotagslink--]

相关文章

  • C#中静态的深入理解

    这篇文章详细的介绍了C#中的静态,有需要的朋友可以参考一下...2020-06-25
  • 浅析C#中静态方法和非静态方法的区别

    C#静态方法与非静态方法的区别不仅仅是概念上的,那么他们有什么具体的区别呢?让我们通过本文向大家介绍下C#中静态方法和非静态方法的区别,一起看看吧...2020-06-25
  • 西部数码空间伪静态配置方法图解

    今天在使用西部数码空间时发现里面有很多定义好的伪静态规则了,下面我来给大家介绍一下在后面主机面板中配置使用伪静态功能吧,希望文章对各位会带来帮助。...2016-10-10
  • Apache在httpd.conf配置文件中设置伪静态(Rewrite)

    .htaccess文件应该被用在内容提供者需要针对特定目录改变服务器的配置而又没有root权限的情况下。如果服务器管理员不愿意频繁修改配置,则可 以允许用户通过.htaccess文件自...2016-01-28
  • Spring-AOP 静态正则表达式方法如何匹配切面

    这篇文章主要介绍了Spring-AOP 静态正则表达式方法如何匹配切面的操作,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...2021-07-19
  • Win7/Windows2003下IIS6.0、IIS7.5的伪静态组件安装和伪静态配置方法

    Win7Windows2003下IIS6.0、IIS7.5的伪静态组件安装和伪静态配置方法,还包括常用的伪静态规则使用方法...2016-01-27
  • php 类的静态变量的初始化

    共有的成员还有办法解决,例如: class A { static public $child; } A::$child = new B(); 对于私有的成员似乎就没有什么干净的方法了,只能这样做: class A {...2016-11-25
  • iis伪静态中文url出现乱码的解决办法

    这篇文章主要介绍了iis伪静态中文url出现乱码的解决办法,需要的朋友可以参考下...2017-07-06
  • apache .htaccess 伪静态页

    apache .htaccess 伪静态页 静态页的地址: http://211.166.45.10/company/new/new_10000.html 指定的动态地址:php?id=10000">http://211.166.45.10/company/new.php?id=...2016-01-28
  • C++中静态初始化数组与动态初始化数组详解

    今天小编就为大家分享一篇C++中静态初始化数组与动态初始化数组详解,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-04-25
  • 如何让iis支持伪静态和增加伪静态规则方法

    如果想让iis支持伪静态就必须 先安装php教程 for iis http://www.php.net/downloads.php 然后安装 微软的 urlrewrite http://www.iis.net/download/urlrewrite 下载...2016-09-20
  • PHP静态分析与跨站脚本检测

    最近在看PHP静态分析与跨站脚本检测的东西,用的是维也纳大学一个博士生做出来的Pixy,这个东西是开源的,而且也作了好几年了,功能逐渐增强。现在这个3.0.3版本里...2016-11-25
  • C#中实现伪静态页面两种方式介绍

    伪静态技术的诞生,带动了于搜索引擎友好C#中实现伪静态页面有两种方式,本文将一一详解,感兴趣的朋友可以参考下,希望本文对你学习伪静态有所帮助...2021-09-22
  • python编程普通及类和静态方法示例详解

    普通方法会将实例传入方法当中(通常用self表示),类方法会将类传入方法当中(通常用cls表示),静态方法中传入与类无关的变量。下面将举例详细说明...2021-10-12
  • apache下开启.htaccess 伪静态支持配置方法

    1、首先确定Apache是否加载了Mod_rewrite 模块 方法: 检查 httpd.conf 中是否存在以下两段代码 (具体路径可能会有所不同,但形式基本是一样的): (一)LoadModule rewrite_modu...2016-01-28
  • 伪静态几种做法

    很多seoer都说把文件做成静态这样对搜索引擎是最好的了,但是像我们这些买别人的虚拟空间,是有限的但是又想以静态形式,如果生成真正的静态如果你的网站有1000000篇文件...2016-11-25
  • C++中静态成员函数访问非静态成员的实例

    这篇文章主要介绍了C++中静态成员函数访问非静态成员的实例的相关资料,需要的朋友可以参考下...2020-04-25
  • apache+php 伪静态写法与iis+php 伪静态区别

    二者没有多大的区别,特别是在前段,后段把以前apache .php?id=$1在iis改成了php\\?id=$1[N,I]写法。 apache+php教程 伪静态写法与iis+php 伪静态区别 这是apache+p...2016-09-20
  • 什么情况下应该把网站的页面变成静态网页发布

    WEB界面静态化进入最后的环节,可以令管理员分门分类分子网地选择项目进行静态化,主要是首页,效率很高,按由XML设定的静态化项目,一个网站管理员可以在几分钟内完成上...2016-09-20
  • php伪静态之APACHE篇

    1.检测Apache是否支持mod_rewrite通过php提供的phpinfo()函数查看环境配置,通过Ctrl+F查找到“Loaded Modules”,其中列出了所有apache2handler已经开启的模块,如果里面包括“mod_rewrite”,则已经支持,不再需要继续设置。...2014-06-07