mock测试_mock对象2

此处使用Mockito示例

一、mockito简介                                                                                                                                                                                                                  

  Mockito 是一种 Java mock 框架,用于创建和配置 mock 对象,主要是用来做 mock 测试的,它可以模拟任何 Spring 管理的 bean、模拟方法的返回值、模拟抛出异常...等,它同时也会记录调用这些模拟方法的参数、调用顺序,从而可以校验出这个 mock 对象是否有被正确的顺序调用,以及按照期望的参数被调用。

  像是 Mockito 可以在单元测试中模拟一个 service 返回的数据,而不会真正去调用该 service,这就是上面提到的 mock 测试精神,也就是通过模拟一个假的 service 对象,来快速的测试当前我想要测试的类

  目前在 Java 中主流的 mock 测试工具有 Mockito、JMock、EasyMock..等,而 SpringBoot 目前内建的是 Mockito 框架。

  Mockito 是 GitHub 上使用最广泛的 Mock 框架,并与 JUnit 结合使用。使用 Mockito 简化了具有外部依赖的类的测试开发。

二、使用 Mockito 的步骤

  1. 模拟任何外部依赖并将这些模拟对象插入测试代码中

  2. 执行测试中的代码

  3. 验证代码是否按照预期执行

三、简单应用

 1. 引入POM

<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-all</artifactId>
    <version>1.10.19</version>
</dependency>

 2. mock对象代码

public class SimpleTest {
    @Test
    public void test() {
        // 创建Mock对象,参数可以是类或者接口
        List<String> list = mock(List.class);
 
        // 设置方法的预见期返回值
        when(list.get(0)).thenReturn("zuozewei");
        when(list.get(1)).thenThrow(new RuntimeException("test exception"));
        String result = list.get(0);
 
        // 验证方法调用
        verify(list).get(0);
 
        // 断言,list的第一个元素是否是"zuozewei"
        Assert.assertEquals(result, "zuozewei");
    }
}

  新建测试类,构造了 list 这样的对象,并且给一个元素赋值 zuozewei。在最后断言的时候,也可以断言这个 list 里面确实有这个值。所以,通过这种方式,我们可以进行对象构造。可以是类,也可以是接口。除了构造对象,当然也可以对方法设定的返回值指定异常。上述代码的意思就是当调用 list 的第二个元素的时候,抛出一个运行异常。

  上面列举了 Mockito 的简单用法。

四、复杂应用

  在 SpringBoot 单元测试中使用 Mockito

  对于比较复杂的用法,可以通过官网深入学习。因为 Mockito 主要用于单元测试,开发人员用的比较多。

 1. 首先在 pom.xml 下新增 spring-boot-starter-test 依赖,该依赖内就有包含了 JUnit、Mockito

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>

 2. 先写好一个 UserService,它里面有两个方法 getUserById() 和 insertUser(),而它们会分别再去调用 UserDao 这个 bean的 getUserById() 和 insertUser() 方法

@Component
public class UserService {
    
    @Autowired
    private UserDao userDao;

    public User getUserById(Integer id) {
        return userDao.getUserById(id);
    }

    public Integer insertUser(User user) {
        return userDao.insertUser(user);
    }
}

 3. User model 的定义如下

public class User {
    private Integer id;
    private String name;
    //省略 getter/setter
}

 4. 如果这时候我们先不使用 Mockito 模拟一个假的 userDao bean,而是真的去调用一个正常的 Spring bean 的 userDao 的话,测试类写法如下。其实就是很普通的注入 userService bean,然后去调用它的方法,而它会再去调用 userDao 取得数据库的数据,然后我们再对返回结果做 assert 断言检查

@RunWith(SpringRunner.class)
@SpringBootTest
public class UserServiceTest {

    //先普通的注入一个userService bean
    @Autowired
    private UserService userService;

    @Test
    public void getUserById() throws Exception {
        //普通的使用userService,他里面会再去调用userDao取得数据库的数据
        User user = userService.getUserById(1);
        
        //检查结果
        Assert.assertNotNull(user);
        Assert.assertEquals(user.getId(), new Integer(1));
        Assert.assertEquals(user.getName(), "John");
    }
}

 5. 但是如果 userDao 还没写好,又想先测 userService 的话,就需要使用 Mockito 去模拟一个假的 userDao 出来

    使用方法是在 userDao 上加上一个 @MockBean 注解,当 userDao 被加上这个注解之后,表示 Mockito 会帮我们创建一个假的 mock 对象,替换掉 Spring 中已存在的那个真实的 userDao bean,也就是说,注入进 userService 的 userDao bean,已经被我们替换成假的 mock 对象了,所以当我们再次调用 userService 的方法时,会去调用的实际上是 mock userDao bean 的方法,而不是真实的 userDao bean

Mockito库中创建模拟对象的三种方式:mock()方法、@Mock注解和@MockBean注解。
Mockito.mock()方法:允许我们在测试中指定方法行为并验证调用。
@Mock注解:用于模拟不属于Spring上下文的对象。实际上是Mockito.mock()方法的缩写,简化了mock对象的创建,但需配合MockitoJUnitRunner或手动初始化。
@MockBean:用于模拟属于Spring Boot应用程序中的Spring上下文的对象,将@Service、@Reporitory、@Component注释的实际beans替换为mock对象。与Spring集成测试,它可以将模拟对象注入到Spring上下文中,替代或新增实际bean。
@Mock和@InjectMocks的区别:
    @Mock:创建一个模拟对象
    @InjectMocks:创建一个该类的实例,并将使用@Mock注解创建的模拟对象注入到该实例中。@RunWith(MockitoJUnitRunner.class)
注意:必须使用@RunWith(MockitoJUnitRunner.class)或MockitoAnnotations.initMocks(this)进行mocks的初始化和注入。

    当我们创建了一个假的 userDao 后,我们需要为这个 mock userDao 自定义方法的返回值,这里有一个公式用法,下面这段代码的意思为:当调用了某个 mock 对象的方法时,就回传给我们一个自定义结果。

Mockito.when( mock对象.方法名() ).thenReturn( 自定义结果 )

 6. 使用 Mockito 模拟 bean 的单元测试具体实例如下

@RunWith(SpringRunner.class)
@SpringBootTest
public class UserServiceTest {
    
    @Autowired
    private UserService userService;
    
    @MockBean   //创建mock对象
    private UserDao userDao;

    @Test
    public void getUserById() throws Exception {
        // 定义当调用mock userDao的getUserById()方法,并且参数为3时,就返回id为200、name为I'm mock3的user对象
        Mockito.when(userDao.getUserById(3)).thenReturn(new User(200, "I'm mock 3"));
        
        // 返回的会是名字为I'm mock 3的user对象,若此处入参传2,则返回的user为null
        User user = userService.getUserById(3);

        Assert.assertNotNull(user);
        Assert.assertEquals(user.getId(), new Integer(200));
        Assert.assertEquals(user.getName(), "I'm mock 3");
    }
}

 7. thenReturn 系列方法

    当使用任何整数值调用 userDao 的 getUserById() 方法时,就回传一个名字为 I'm mock3 的 user 对象:

Mockito.when(userDao.getUserById(Mockito.anyInt())).thenReturn(new User(3, "I'm mock"));
User user1 = userService.getUserById(3); // 回传的user的名字为I'm mock
User user2 = userService.getUserById(200); // 回传的user的名字也为I'm mock

    限制只有当参数的数字是 3 时,才会回传名字为 I'm mock 3 的 user 对象:

Mockito.when(userDao.getUserById(3)).thenReturn(new User(3, "I'm mock"));
User user1 = userService.getUserById(3); // 回传的user的名字为I'm mock
User user2 = userService.getUserById(200); // 回传的user为null

    当调用 userDao 的 insertUser() 方法时,不管传进来的 user 是什么,都回传 100:

Mockito.when(userDao.insertUser(Mockito.any(User.class))).thenReturn(100);
Integer i = userService.insertUser(new User()); //会返回100

 8. thenThrow 系列方法

    当调用 userDao 的 getUserById() 时的参数是 9 时,抛出一个 RuntimeException:

Mockito.when(userDao.getUserById(9)).thenThrow(new RuntimeException("mock throw exception"));
User user = userService.getUserById(9); //会抛出一个RuntimeException

    如果方法没有返回值的话(即是方法定义为public void myMethod() {...}),要改用 doThrow() 抛出 Exception:

Mockito.doThrow(new RuntimeException("mock throw exception")).when(userDao).print();
userService.print(); //会抛出一个RuntimeException

 9. verify 系列方法

  验证方法调用:

Mockito.verify(userDao).getUserById(Mockito.eq(3)) ;
Mockito.verify(userDao).getUserById(3);

  验证调用次数,下文表示检查调用 userDao 的 getUserById()、且参数为3的次数是否为1次:

Mockito.verify(userDao, Mockito.times(1)).getUserById(Mockito.eq(3)) ;

  验证调用顺序,验证 userDao 是否先调用 getUserById() 两次,并且第一次的参数是 3、第二次的参数是 5,然后才调用insertUser() 方法:

InOrder inOrder = Mockito.inOrder(userDao);
inOrder.verify(userDao).getUserById(3);
inOrder.verify(userDao).getUserById(5);
inOrder.verify(userDao).insertUser(Mockito.any(User.class));

五、Mockito常用参数匹配器

  • any()anyInt()anyString() - 匹配任意值
  • eq() - 严格匹配
  • isNull()isNotNull() - 空值检查
  • contains()endsWith() - 字符串匹配
  • argThat() - 自定义匹配逻辑

 

posted on 2025-05-27 01:08  花溪月影  阅读(110)  评论(0)    收藏  举报