【问题标题】:Junit Mock Repeated Feign Client Calls but Only the Last Mock Returned Multiple TimesJunit Mock 重复 Feign 客户端调用,但只有最后一个 Mock 多次返回
【发布时间】:2019-10-19 19:36:31
【问题描述】:

我有一个 Spring Boot 应用程序,它使用 Feign Client 调用微服务以在创建新部门时将用户添加到 User 表中(新部门将插入到 Department 表中)。请求如下所示:

请求:

 {
  "department": "math",
  "usernameList": ["aaa", "bbb", "ccc"]
 }

用户模型:

 public class User {
    private String username;
 }

Feign 客户端:

 import org.springframework.cloud.openfeign.FeignClient;
 @FeignClient(name = "user-client", url = "/.../user", configuration = UserConfiguration.class)

 public interface UserClient {

     @RequestMapping(method = RequestMethod.POST, value = "users")
     User createUser(User user);
 }

用户服务:

@Service
public class UserService {

private final UserClient userClient;       

public UserResponse createUser(@Valid Request request);

     List<User> userList = request.getUsernameList()
           .stream()
           .map(username -> userClient.createUser(mapToUser(username)) 
           .collect(Collectors.toList());
 ......
}

上面的代码有效,我能够将 3 个用户添加到数据库中。 userList 有 3 个正确的用户名。但是,当我在下面运行 junit 测试时,似乎只有最后一个 userResp(“ccc”)作为模拟响应返回了 3 次。当我在调试模式下运行junit测试时,我看到每次thenReturn(userResp)都有正确的userResp,但在UserService中,userList最终包含3个“ccc”,而不是“aaa,bbb, cc”。我尝试在 UserService 中使用 FOR 循环而不是流,结果是一样的,所以不是因为流。我还尝试删除 Junit 中的 FOR 循环,只调用了 3 次模拟,结果相同。我不确定这是否与 Feign 客户端嘲笑有关,或者我在测试用例中做错了什么。有人可以帮忙吗?

我的朱尼特:

 public class UserTest {

    @MockBean
    private UserClient userClient;  

    @Test
    public void testAddUser() throws Exception {

        for (int i=1; i<=3; i++) {

             User userResp = new User();

              if (i==1) {
             userResp.setUsername("aaa");
            // mock response
       Mockito.when(userClient.createUser(ArgumentMatchers.any(User.class)))
         .thenReturn(userResp);
           }
           if (i==2) {
             userResp.setUsername("bbb");
            // mock response
       Mockito.when(userClient.createUser(ArgumentMatchers.any(User.class)))
         .thenReturn(userResp);
           }
        if (i==3) {
             userResp.setUsername("ccc");
            // mock response
       Mockito.when(userClient.createUser(ArgumentMatchers.any(User.class)))
         .thenReturn(userResp);
           }
       }

       // invoke the real url
       MvcResult result = mockMvc.perform(post("/users")
            .content(TestUtils.toJson(userRequest, false))
            .contentType(contentType))
            .andDo(print())
            .andExpect(status().isCreated())
            .andReturn();  
    }

【问题讨论】:

  • 它在每种情况下都返回“ccc”,因为您每次都在模拟同一个调用 Mockito.when(userClient.createUser(ArgumentMatchers.any(User.class))) .thenReturn(userResp);并且由于“ccc”是最后一个模拟它返回的“ccc”
  • 当i==1时,userResp有“aaa”,不应该是第一次返回“aaa”吗?或者 Junit 将 3 个调用视为 1 个调用,因为它是同一个模拟调用?
  • 不,因为当您进行实际服务调用时,userResp 的值设置为“ccc”
  • 我明白了。因此,并不是每次服务调用 createUser 时都会调用模拟。在调用服务调用之前执行所有模拟......
  • 没错,我提出了一种可以帮助您处理案件的方法

标签: spring-boot junit spring-cloud-feign


【解决方案1】:

要使方法为后续调用返回不同的值,您可以使用

Mockito.when(userClient.createUser(ArgumentMatchers.any(User.class)))
            .thenReturn("aaa")
            .thenReturn("bbb")
            .thenReturn("ccc"); //any

// Or a bit shorter with varargs:

    Mockito.when(userClient.createUser(ArgumentMatchers.any(User.class)))
        .thenReturn("aaa", "bbb", "ccc"); //any

【讨论】:

  • 这真的可以用吗? mockClient 返回真实对象,编译器不允许您返回字符串,不是吗?当您使用模拟客户端时,您正在模拟真实响应(字符串)并让客户端将其解码为对象。但是当您使用Mockito.when(..) 模拟时,您返回客户端返回对象,并绕过解码过程。有没有办法让 mockClient 使用mockClient.ok(..) 方法在后续调用中返回不同的结果?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-08-15
  • 1970-01-01
  • 1970-01-01
  • 2018-04-16
  • 2020-03-28
相关资源
最近更新 更多