用好 mock 事半功倍

一、常用的mock工具

在Java开发中,Mock工具可用于单元测试和模拟对象的创建

1、Mockito:

        Mockito是Java中最受欢迎的Mocking框架之一。
        提供简单易用的API,可以方便地创建和配置模拟对象。
        支持验证模拟对象的行为和交互。
        提供了注解支持,用于简化测试代码的编写。
        文档丰富,社区活跃,广泛应用于Java项目中,SpringBootTest 默认使用的也是 Mockito
2、EasyMock:

        EasyMock是另一个常用的Java Mocking框架。
        提供了单的API来创建和配置模拟对象。
        支持验证模拟对象的行为和交互。
        使用了基于录制和回放的模拟对象创建方式。
        语法相对于Mockito来说稍微繁琐一些。
        文档相对较少,但社区比较活跃。
3、PowerMock:

        PowerMock是一个扩展了Mockito和EasyMock功能的工具。
        可以模拟静态方法、构造函数、私有方法等。
        PowerMock的使用相对复杂,需要在测试类上使用特定的注解。
        在处理一些特殊场景(如遗留代码)时非常有用。
        文档相对较少,但在特定情况下仍然是一个有价值的工具。

二、spring / 非 spring 场景测试验证

选取的mock工具为 mockito ,其中依赖版本为 mockito-core:3.3.3 


    org.mockito
    mockito-core
    3.3.3
    test

1、场景假设,我们的希望实现用户在线时长查询的方法,其中需要校验用户是否登陆(第三方接口)

@Service
public class LoginService {

    @Resource
    private SSOService ssoService;

    /**
     * 调用单点登陆系统判断
     * @param userId
     * @return
     */
    public boolean isLogin(String userId) {
        UserInfo userInfo = ssoService.getLoginInfo(userId);
        if (Objects.nonNull(userInfo)) {
            return true;
        }
        return false;
    }
}

@Service
public class UserService {

    @Resource
    private LoginService loginService;

    /**
     * 获取用户在线时常
     * @return
     */
    public Integer getUserOnlineTimes(String userId){
        //判断是否登陆
        if(!loginService.isLogin(userId)){
            return 0;
        }
        return  (int)(Math.random() * 100) + 1;
    }
}

2、在没有SSO调用授权之前我们不好进行单元测试,这时就需要用的mock工具了

a、非 spring 方式使用mock进行测试验证,通过反射注入 mock 类

@Slf4j
@RunWith(MockitoJUnitRunner.class)
public class UserServiceTest {

    @Mock
    private LoginService loginServiceMock;

    @Test
    public void getUserOnlineTimes() throws NoSuchFieldException, IllegalAccessException {
        // 设置 mock 场景
        Mockito.when(loginServiceMock.isLogin("zhangsan")).thenReturn(false);
        Mockito.when(loginServiceMock.isLogin("lisi")).thenReturn(true);

        // 反射赋值属性 或 在 userService 作为成员变量,使用 @InjectMocks 注入
        UserService userService = new UserService();
        Field loginService = userService.getClass().getDeclaredField("loginService");
        loginService.setAccessible(true);
        loginService.set(userService, loginServiceMock);

        //验证
        Integer onlineTimes = userService.getUserOnlineTimes("zhangsan");
        Assert.assertEquals(Integer.valueOf(0), onlineTimes);

        onlineTimes = userService.getUserOnlineTimes("lisi");
        Assert.assertTrue(onlineTimes > 0);
    }
}

b、非 spring 方式使用mock进行测试验证,通过 mockito 注解 @InjectMocks 注入

@Slf4j
@RunWith(MockitoJUnitRunner.class)
public class UserServiceTest2 {

    @Mock
    private LoginService loginServiceMock;

    @InjectMocks
    private UserService userService;

    @Test
    public void getUserOnlineTimes() {
        // 设置 mock 场景
        Mockito.when(loginServiceMock.isLogin("zhangsan")).thenReturn(false);
        Mockito.when(loginServiceMock.isLogin("lisi")).thenReturn(true);

        //验证
        Integer onlineTimes = userService.getUserOnlineTimes("zhangsan");
        Assert.assertEquals(Integer.valueOf(0), onlineTimes);

        onlineTimes = userService.getUserOnlineTimes("lisi");
        Assert.assertTrue(onlineTimes > 0);

        // 验证mock方法被执行的次数
        Mockito.verify(loginServiceMock, Mockito.times(1)).isLogin("zhangsan");
    }
}

c、spring 容器中使用mock进行测试验证

@Slf4j
@SpringBootTest
@RunWith(SpringRunner.class)
public class UserServiceTestInSpring {

    @MockBean
    private LoginService loginServiceMock;

    @Resource
    private UserService userService;

    @Before
    public void init(){
        // 启用mock
        MockitoAnnotations.initMocks(this);
    }

    @Test
    public void getUserOnlineTimes() {
        // 设置 mock 场景
        Mockito.when(loginServiceMock.isLogin("zhangsan")).thenReturn(false);
        Mockito.when(loginServiceMock.isLogin("lisi")).thenReturn(true);

        //验证
        Integer onlineTimes = userService.getUserOnlineTimes("zhangsan");
        Assert.assertEquals(Integer.valueOf(0), onlineTimes);

        onlineTimes = userService.getUserOnlineTimes("lisi");
        Assert.assertTrue(onlineTimes > 0);
    }
}
三、mockito 使用总结

Mockito提供了强大的验证功能,支持验证模拟对象的方法调用次数、参数和顺序,以确保代码的预期行为,另外注解的支持可以轻松地创建和注入模拟对象,让测试代码更加简化,但是Mockito本身无法直接模拟静态方法和私有方法。对于这些场景,可以使用 PowerMock 或者使用java代码直接构造生成

你可能感兴趣的:(工具类,#,springboot,mock,mockito,java)