【问题标题】:Mock an Eureka Feign Client for Unittesting模拟 Eureka Feign 客户端进行单元测试
【发布时间】:2016-03-27 15:20:15
【问题描述】:

我正在使用 Spring Cloud 的 eureka 和 feign 在某些服务之间进行通信(比如说 A 和 B)。现在我想对我的单个服务(A)的服务层进行单元测试。问题是,这个服务 (A) 正在使用一个 feign 客户端来请求另一个服务 (B) 的一些信息。

在没有任何特殊配置的情况下运行单元测试会引发以下异常:java.lang.RuntimeException: com.netflix.client.ClientException: Load balancer does not have available server for client: service-b => 但我不希望任何服务器运行。

我的问题是:有没有办法模拟 feign 客户端,所以我可以在不运行 eureka 实例和服务 (B) 的情况下对我的服务 (A) 进行单元测试?

编辑: 我最终为 feign 客户端创建了一个存根。存根被标记为主要组件,以强制在我的测试中实例化存根。
这是我想出的解决方案。

//the feign client
@FeignClient("user") 
public interface UserClient { 
    UserEntity getUser(); 
}

//the implementation i use for the tests 
@Component 
@Primary //mark as primary implementation
public class UserClientTestImpl implements UserClient { 
    @Override public UserEntity getUser() { 
        return someKindOfUser; 
    } 
}

【问题讨论】:

    标签: unit-testing spring-cloud netflix-eureka netflix-feign


    【解决方案1】:

    问题是……你还需要模拟吗?我经常看到人们提到“模拟”作为任何“不应该成为单元测试的一部分”的第一个解决方案。 Mocking 是一种技术,而不是解决所有问题的方法。 (见here)。

    如果您仍处于代码的早期阶段,只需重构并使用其他东西,而不是依赖于 Feign 客户端的具体实例。你可以使用接口、抽象类、特征或任何你想要的东西。不要依赖对象本身,否则你必须“模拟它”。

    public interface IWebClient {
      public String get(...);
      public String post(...);
    } 
    

    对于这个问题:但是我将有其他代码完全相同(除了它将在 Feign 的具体实例上),那我该怎么办? 好吧,您可以编写功能测试并调用可以在本地设置的 Web 服务器实例 - 或使用 Wiremock,正如 Marcin Grzejszczak 在其中一个答案中提到的那样。

    public class FeignClientWrapper implements IWebClient {
      private feign = something
    
      public String get() {
        feign.get( ... ) 
      }
    
      public String post() {
        feign.post( ... ) 
      }
    } 
    

    单元测试用于测试算法、if/else、循环:单元如何工作。不要编写代码来使模拟适合 - 它必须相反:您的代码应该具有较少的依赖关系,并且仅在需要验证行为时才应该模拟(否则您可以使用存根或假对象):你需要验证行为吗?您是否需要测试代码中是否调用了特定方法?或者一个特定的方法被 X、Y 和 Z 连续调用了 3 次?好吧,那么是的,嘲笑是可以的。

    否则,请使用假对象:您想要测试的只是调用/响应,也许还有状态码。您可能想要的只是测试您的代码如何响应不同的输出(例如,字段“错误”是否存在于 JSON 响应中)、不同的状态代码(假设客户端文档是正确的:GET 时为 200 OK,201当 POST 等)。

    【讨论】:

    • 这是我目前想出的解决方案: feign 接口@FeignClient("user") public interface UserClient { //some feign annotations UserEntity getUser(); } 我用于测试的实现@Component @Primary public class UserClientTestImpl implements UserClient { @Override public UserEntity getUser() { return someKindOfUser; } } 基本上就是你提到的@Markon 方法。
    • 您可以在您的问题中发布代码吗?很难阅读 cmets 中的代码 :D 我很高兴它有所帮助。当你想测试行为时,你可以使用模拟。如果您想测试“连接”,请设置一个小型网络服务器! :P
    • 我需要模拟一个 feignClient 的响应,这样我就不会启动其他微服务。它有什么问题?我也不想在这样一个简单的情况下使用外部附加库。
    • @KirillCh 如果你依赖对象本身,你需要模拟它。如果你依赖于接口,那么它就变得微不足道了。在这种情况下,您将不需要任何外部库,甚至不需要 mockito!和往常一样,这取决于你想测试什么;使用要测试行为的模拟。
    • 谢谢。我可以问一个官方问题并在下面给你一个链接,因为它对我很重要但我不明白你。好吗?
    【解决方案2】:

    模拟 feign 客户端在微服务组件测试中非常有用。您想测试一个微服务,而不必启动所有其他微服务。

    如果您使用的是 Spring(看起来确实如此),那么 @MockBean 注释和一些 Mockito 代码就可以完成这项工作。

    @RunWith(SpringRunner.class)
    @SpringBootTest(webEnvironment = 
    SpringBootTest.WebEnvironment.DEFINED_PORT)
    public class TestYourComponent {
        @Configuration
        @Import({YourConfiguration.class})
        public static class TestConfiguration {
        }
    
        @MockBean
        private UserClient userClient;
    
        @Test
        public void someTest()
        {
            //...
            mockSomeBehavior();
            //...
        }
    
        private void mockSomeBehavior() {
            Mockito.doReturn(someKindOfUser).when(userClient).getUser();
        }
    }
    

    【讨论】:

    • 通常,这不是单元测试应该做的。您甚至可以通过查看测试的依赖关系来真正看到这一点:SpringBootTest 应该在这里敲响警钟,因为您正在初始化整个 springboot 上下文(+ 所有 bean 等)。在单元测试中你需要什么?这更多是关于集成/组件测试”,而不是单元测试。
    • @Markon,如果您阅读我写的内容,您会发现我说的是组件测试,而不是单元测试。也许您打算回答最初的问题,该问题确实声称与单元测试有关? ...虽然,很明显,最初的提问者也意味着组件测试,即使他们使用了错误的词。
    • 是的,我知道这是关于组件测试的。我只是想在您的答案中添加一些内容,因为我看到越来越多的人对组件测试(就像您编写的那样)单元测试感到困惑。而且我也知道你知道区别:)
    【解决方案3】:

    如果您需要使用模拟,您可以使用 Wiremock 来存根给定请求的响应 - http://wiremock.org/stubbing.html。这样,您将使用发送的真实 HTTP 请求进行集成测试。对于单元测试,@Markon 的答案非常好。

    【讨论】:

      猜你喜欢
      • 2018-09-07
      • 1970-01-01
      • 1970-01-01
      • 2021-09-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-06-01
      • 2019-10-14
      相关资源
      最近更新 更多