模拟Eureka Feign客户进行单元测试

时间:2015-12-21 14:06:25

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

我正在使用spring cloud的eureka并假装在一些服务之间进行通信(比方说A和B)。现在我喜欢单一测试单个服务的服务层(A)。问题是,该服务(A)正在使用假装客户端请求其他服务的某些信息(B)。

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

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

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

//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; 
    } 
}

3 个答案:

答案 0 :(得分:5)

问题是......你甚至需要嘲笑吗?我经常看到人们提到" mock"作为任何事情的第一个解决方案"不应该是单元测试的一部分"。模拟是一种技术,而不是一切的解决方案。 (见here)。

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

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,循环:单位如何工作。不要编写代码来使模拟适合 - 它必须是相反的方式:您的代码应该具有较少的依赖性,并且您应该仅在需要验证行为时进行模拟(否则您可以使用存根或伪对象):你需要验证行为吗?您是否需要测试代码中是否调用了特定方法?或者连续3次使用X,Y和Z调用特定方法?那么,是的,嘲笑是可以的。

否则,请使用假对象:您想要的只是测试呼叫/响应以及状态代码。你可能想要的只是测试你的代码如何对不同的输出作出反应(例如,字段"错误"在JSON响应中是否存在),不同的状态代码(假设客户文档是正确的:200 GET时可以,POST时等201)。

答案 1 :(得分:2)

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

答案 2 :(得分:2)

模拟假装客户端在微服务组件测试中非常有用。您想测试一个微服务,而不必启动所有其他微服务。如果你正在使用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();
    }
}