模拟 Eureka Feign 客户端以进行单元测试
Mock an Eureka Feign Client for Unittesting
我正在使用 spring 云的 eureka 并假装在某些服务(假设 A 和 B)之间进行通信。现在我想对单个服务 (A) 的服务层进行单元测试。问题是,此服务 (A) 使用假客户端请求其他服务 (B) 的一些信息。
运行 没有任何特殊配置的单元测试抛出以下异常:java.lang.RuntimeException: com.netflix.client.ClientException: Load balancer does not have available server for client: service-b
=> 但我不希望任何服务器 运行.
我的问题是:有没有办法模拟假客户端,这样我就可以在没有 运行 一个尤里卡实例和服务 (B) 的情况下对我的服务 (A) 进行单元测试?
编辑:
我最终为假装客户创建了一个存根。存根被标记为主要组件,以强制 spring 在我的测试中实例化存根。
这是我想出的解决方案。
//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;
}
}
问题是……你还需要模拟吗?我经常看到人们提到 "mock" 作为任何 "should not be part of the unit test" 的第一个解决方案。模拟是一种技术,而不是解决所有问题的方法。 (参见 here)。
如果您仍处于代码的早期阶段,只需重构并使用其他东西,而不是依赖于 Feign Client 的具体实例。您可以使用接口、抽象 class、特征或任何您想要的东西。不要依赖对象本身,否则你必须 "mock it".
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,循环:units 是如何工作的。不要编写代码来使模拟适合——它必须是相反的:你的代码应该有更少的依赖性,你应该只在你需要验证行为时模拟(否则你可以使用存根或假对象):您需要验证行为吗?您是否需要测试代码中是否调用了特定方法?或者连续 3 次使用 X、Y 和 Z 调用特定方法?好吧,那是的,嘲笑是可以的。
否则,使用假对象:您想要的只是测试 call/response 和状态代码。您可能想要的只是测试您的代码如何对不同的输出做出反应(例如,字段 "error" 是否存在于 JSON 响应中)、不同的状态代码(假设客户端文档是正确的: GET 时 200 OK,POST 时 201,等等)。
如果您需要使用模拟,您可以使用 Wiremock 来存根给定请求的响应 - http://wiremock.org/stubbing.html。这样您就可以使用发送的真实 HTTP 请求进行集成测试。对于单元测试,@Markon 的回答非常好。
模拟假客户端在微服务组件测试中非常有用。您希望在不启动所有其他微服务的情况下测试一个微服务。
如果您正在使用 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();
}
}
我正在使用 spring 云的 eureka 并假装在某些服务(假设 A 和 B)之间进行通信。现在我想对单个服务 (A) 的服务层进行单元测试。问题是,此服务 (A) 使用假客户端请求其他服务 (B) 的一些信息。
运行 没有任何特殊配置的单元测试抛出以下异常:java.lang.RuntimeException: com.netflix.client.ClientException: Load balancer does not have available server for client: service-b
=> 但我不希望任何服务器 运行.
我的问题是:有没有办法模拟假客户端,这样我就可以在没有 运行 一个尤里卡实例和服务 (B) 的情况下对我的服务 (A) 进行单元测试?
编辑:
我最终为假装客户创建了一个存根。存根被标记为主要组件,以强制 spring 在我的测试中实例化存根。
这是我想出的解决方案。
//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;
}
}
问题是……你还需要模拟吗?我经常看到人们提到 "mock" 作为任何 "should not be part of the unit test" 的第一个解决方案。模拟是一种技术,而不是解决所有问题的方法。 (参见 here)。
如果您仍处于代码的早期阶段,只需重构并使用其他东西,而不是依赖于 Feign Client 的具体实例。您可以使用接口、抽象 class、特征或任何您想要的东西。不要依赖对象本身,否则你必须 "mock it".
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,循环:units 是如何工作的。不要编写代码来使模拟适合——它必须是相反的:你的代码应该有更少的依赖性,你应该只在你需要验证行为时模拟(否则你可以使用存根或假对象):您需要验证行为吗?您是否需要测试代码中是否调用了特定方法?或者连续 3 次使用 X、Y 和 Z 调用特定方法?好吧,那是的,嘲笑是可以的。
否则,使用假对象:您想要的只是测试 call/response 和状态代码。您可能想要的只是测试您的代码如何对不同的输出做出反应(例如,字段 "error" 是否存在于 JSON 响应中)、不同的状态代码(假设客户端文档是正确的: GET 时 200 OK,POST 时 201,等等)。
如果您需要使用模拟,您可以使用 Wiremock 来存根给定请求的响应 - http://wiremock.org/stubbing.html。这样您就可以使用发送的真实 HTTP 请求进行集成测试。对于单元测试,@Markon 的回答非常好。
模拟假客户端在微服务组件测试中非常有用。您希望在不启动所有其他微服务的情况下测试一个微服务。
如果您正在使用 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();
}
}