51Testing软件测试论坛

 找回密码
 (注-册)加入51Testing

QQ登录

只需一步,快速开始

微信登录,快人一步

手机号码,快捷登录

查看: 2140|回复: 1
打印 上一主题 下一主题

[转贴] Java mocking 单元测试框架介绍

[复制链接]
  • TA的每日心情
    无聊
    2024-9-27 10:07
  • 签到天数: 62 天

    连续签到: 1 天

    [LV.6]测试旅长

    跳转到指定楼层
    1#
    发表于 2018-3-7 15:20:57 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
    我需要在数据库里插入一些数据,基于这些真实的数据我才可以进行单元测试,而且每次都需要重复
    准备脚本,还要考虑数据的清理。
    我需要启动一个Tomcat,然后通过Http Client发送请求,然后观察数据,才可以进行测试。
    我需要启动Dubbo服务的提供者,因为我的类里依赖的这个服务,否则我的类没办法正常运行。
    我需要依赖第三方的一个接口,因为我这个类是用来发送短信的,这样我才能验证我的短信发送时正
    常的。往往第三方都没有测试环境,即使有的话也不稳定,导致我不能经常进行持续集成的测试。
    ……
    我们主要的面临的问题是,由于所依赖的组件不容易构造或者不容易获取,所以导致我们不容易写出
    单元测试,甚至由于觉得麻烦索性不写单元测试(悲伤),如:
    HttpServletRequest必须在web容器中才能构造出来;
    Dubbo服务必须有服务提供者;
    依赖第三方的一个接口,如短信,支付等。http接口, webservice接口等;
    JDBC中的ResultSet对象;

    Mock测试就是在测试过程中,对于某些不容易构造或者不容易获取比较复杂的对象,用一个虚拟的对
    象来创建以便测试的测试方法。
    Mock最大的功能是帮你把单元测试的解耦,如果你的代码对另一个类或者接口有依赖,它能够帮你模
    拟这些依赖,并帮你验证所调用的依赖的行为。

    Mcokito框架使用:

    验证行为
    1. import static org.mockito.Mockito.*;

    2. public void once_created_mock_will_remember_all_interactions() {
    3.     List<String> mockedList = mock(List.class);

    4.     mockedList.add("one");
    5.     mockedList.clear();

    6.     //验证调用了add方法,并且参数为one
    7.     verify(mockedList).add("one");
    8.     verify(mockedList).clear();

    9.     verify(mockedList).add("two");
    10. }
    复制代码
    打桩–>Stubbing
    1. public void stubbing() {
    2.     LinkedList mockedList = mock(LinkedList.class);

    3.     when(mockedList.get(0)).thenReturn("first");
    4.     when(mockedList.get(0)).thenReturn("second");

    5.     System.out.println(mockedList.get(0));

    6.     //mock对象会覆盖整个被mock的对象,因此没有stub的方法只能返回默认值。
    7.     System.out.println(mockedList.get(999));

    8.     when(mockedList.get(1)).thenThrow(new RuntimeException());

    9.     System.out.println(mockedList.get(1));
    10. }
    复制代码
    如果方法返回值没有stub过,mockito会返回相应的默认值。如int会返回0,布尔值返回false。对于其
    他类型会返回null。重复stub两次,则以第二次为准。

    参数匹配器
    1. public void argument_matcher() {
    2.     LinkedList mockedList = mock(LinkedList.class);

    3.     //设置无论获取任何位置的值,都返回固定的一个fixed-element
    4.     when(mockedList.get(anyInt())).thenReturn("fixed-element");

    5.     System.out.println(mockedList.get(999));

    6.     verify(mockedList).get(anyInt());

    7.     //自定义参数匹配器
    8.     when(mockedList.contains(argThat(isValid()))).thenReturn(true);

    9.     System.out.println(mockedList.contains("custom-matcher"));

    10.     System.out.println(mockedList.contains("custom-matcher2"));
    11. }

    12. 顺序调用

    13. public void verify_invoke_in_order() {

    14.     List singleMock = mock(List.class);

    15.     singleMock.add("was added first");
    16.     singleMock.add("was added second");

    17.     InOrder inOrder = inOrder(singleMock);

    18.     inOrder.verify(singleMock).add("was added first");
    19.     inOrder.verify(singleMock).add("was added second");
    20. }
    复制代码
    注解的使用

    @mock:需要被Mock的对象,mock对象的方法不会再被真实调用,如果需要调用需要使用stub(打桩);
    @InjectMocks: 需要将Mock对象注入的对象;
    @Captor:(参数捕获器)捕获方法参数进行验证;

    @mock与@InjectMocks使用如下:



    现在假设在service层有如下代码需要测试:
    1. public boolean isEmployee(String phoneNumber) {
    2.         if (StringUtils.isEmpty(phoneNumber)) {
    3.             return false;
    4.         }
    5.         return userRepository.findByPhoneNumber(phoneNumber).isPresent();
    6.     }
    复制代码
    测试代码
    1. @Mock
    2. private userRepository userRepository;
    3. @InjectMocks
    4. private userServiceImpl userServiceImpl;

    5. @Test
    6. public void testMock() {
    7.       User user = new User();
    8.       stub(userRepository
    9.                 .findByPhoneNumber(anyString()))
    10.                 .toReturn(Optional.of(user));
    11.       userServiceImpl.isEmployee("110");
    12.       verify(userRepository,times(1))
    13.      .findByPhoneNumber("110");//findByPhoneNumber方法被调用一次
    14.     }
    复制代码
    如上可以正常测试。但是将最后一行findByPhoneNumber(“110”)号码改成119酒会报错,如下:

    单元测试三步骤:设置测试数据,设定预期结果,验证结果,三步通过,测试通过。

    @Captor使用如下:
    ArgumentCaptor的Api
    argument.capture() 捕获方法参数
    argument.getValue() 获取方法参数值,如果方法进行了多次调用,它将返回最后一个参数值
    argument.getAllValues() 方法进行多次调用后,返回多个参数值
    代码案例:
    1. @Captor
    2. private ArgumentCaptor<Money> moneyCaptor;
    3. @Captor
    4. private ArgumentCaptor<User> userCaptor;


    5. verify(userMoneyService, times(1)).updateStatus( userCaptor.capture(),moneyCaptor.capture());//获取参数
    6. assertThat(userCaptor.getValue().getRole(), is("Admin");//验证参数
    7. assertThat(moneyCaptor.getValue(), is("15rmb");

    8. //updateStatus方法如下
    9. public void updateStatus(User user,Money money){
    10.     执行方法……
    11. }
    复制代码


    本帖子中包含更多资源

    您需要 登录 才可以下载或查看,没有帐号?(注-册)加入51Testing

    x
    分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
    收藏收藏
    回复

    使用道具 举报

    本版积分规则

    关闭

    站长推荐上一条 /1 下一条

    小黑屋|手机版|Archiver|51Testing软件测试网 ( 沪ICP备05003035号 关于我们

    GMT+8, 2024-11-5 16:30 , Processed in 0.069309 second(s), 23 queries .

    Powered by Discuz! X3.2

    © 2001-2024 Comsenz Inc.

    快速回复 返回顶部 返回列表