Bas*_*igt 21 java wiremock netflix-eureka feign spring-cloud-netflix
我使用Spring-Cloud-Netflix进行微服务之间的通信.假设我有两个服务,Foo和Bar,而Foo使用Bar的REST端点之一.我使用一个带注释的接口@FeignClient:
@FeignClient
public interface BarClient {
@RequestMapping(value = "/some/url", method = "POST")
void bazzle(@RequestBody BazzleRequest);
}
Run Code Online (Sandbox Code Playgroud)
然后我SomeService在Foo中有一个服务类,它调用了BarClient.
@Component
public class SomeService {
@Autowired
BarClient barClient;
public String doSomething() {
try {
barClient.bazzle(new BazzleRequest(...));
return "so bazzle my eyes dazzle";
} catch(FeignException e) {
return "Not bazzle today!";
}
}
}
Run Code Online (Sandbox Code Playgroud)
现在,为了确保服务之间的通信正常工作,我想构建一个测试,使用WireMock之类的东西,对一个假的Bar服务器发出真正的HTTP请求.测试应确保假设正确解码服务响应并报告SomeService.
public class SomeServiceIntegrationTest {
@Autowired SomeService someService;
@Test
public void shouldSucceed() {
stubFor(get(urlEqualTo("/some/url"))
.willReturn(aResponse()
.withStatus(204);
String result = someService.doSomething();
assertThat(result, is("so bazzle my eyes dazzle"));
}
@Test
public void shouldFail() {
stubFor(get(urlEqualTo("/some/url"))
.willReturn(aResponse()
.withStatus(404);
String result = someService.doSomething();
assertThat(result, is("Not bazzle today!"));
}
}
Run Code Online (Sandbox Code Playgroud)
如何将这样的WireMock服务器注入eureka,以便假装能够找到它并与之通信?我需要什么样的注释魔法?
mla*_*dzo 16
下面是使用WireMock通过Feign客户端和Hystrix回退测试SpringBoot配置的示例.
如果您使用Eureka作为服务器发现,则需要通过设置属性来禁用它"eureka.client.enabled=false".
首先,我们需要为我们的应用程序启用Feign/Hystrix配置:
@SpringBootApplication
@EnableFeignClients
@EnableCircuitBreaker
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
@FeignClient(
name = "bookstore-server",
fallback = BookClientFallback.class,
qualifier = "bookClient"
)
public interface BookClient {
@RequestMapping(method = RequestMethod.GET, path = "/book/{id}")
Book findById(@PathVariable("id") String id);
}
@Component
public class BookClientFallback implements BookClient {
@Override
public Book findById(String id) {
return Book.builder().id("fallback-id").title("default").isbn("default").build();
}
}
Run Code Online (Sandbox Code Playgroud)
请注意,我们正在为Feign客户端指定一个回退类.每当Feign客户端调用失败(例如连接超时)时,都会调用Fallback类.
为了使测试工作,我们需要配置Ribbon负载均衡器(在发送http请求时将由Feign客户端在内部使用):
@RunWith(SpringRunner.class)
@SpringBootTest(properties = {
"feign.hystrix.enabled=true"
})
@ContextConfiguration(classes = {BookClientTest.LocalRibbonClientConfiguration.class})
public class BookClientTest {
@Autowired
public BookClient bookClient;
@ClassRule
public static WireMockClassRule wiremock = new WireMockClassRule(
wireMockConfig().dynamicPort()));
@Before
public void setup() throws IOException {
stubFor(get(urlEqualTo("/book/12345"))
.willReturn(aResponse()
.withStatus(HttpStatus.OK.value())
.withHeader("Content-Type", MediaType.APPLICATION_JSON)
.withBody(StreamUtils.copyToString(getClass().getClassLoader().getResourceAsStream("fixtures/book.json"), Charset.defaultCharset()))));
}
@Test
public void testFindById() {
Book result = bookClient.findById("12345");
assertNotNull("should not be null", result);
assertThat(result.getId(), is("12345"));
}
@Test
public void testFindByIdFallback() {
stubFor(get(urlEqualTo("/book/12345"))
.willReturn(aResponse().withFixedDelay(60000)));
Book result = bookClient.findById("12345");
assertNotNull("should not be null", result);
assertThat(result.getId(), is("fallback-id"));
}
@TestConfiguration
public static class LocalRibbonClientConfiguration {
@Bean
public ServerList<Server> ribbonServerList() {
return new StaticServerList<>(new Server("localhost", wiremock.port()));
}
}
}
Run Code Online (Sandbox Code Playgroud)
功能区服务器列表需要匹配WireMock配置的URL(主机和端口).
下面是一个如何使用随机端口连接Feign和WireMock的示例(基于Spring-Boot github答案).
@RunWith(SpringRunner.class)
@SpringBootTest(properties = "google.url=http://google.com") // emulate application.properties
@ContextConfiguration(initializers = PortTest.RandomPortInitializer.class)
@EnableFeignClients(clients = PortTest.Google.class)
public class PortTest {
@ClassRule
public static WireMockClassRule wireMockRule = new WireMockClassRule(
wireMockConfig().dynamicPort()
);
@FeignClient(name = "google", url = "${google.url}")
public interface Google {
@RequestMapping(method = RequestMethod.GET, value = "/")
String request();
}
@Autowired
public Google google;
@Test
public void testName() throws Exception {
stubFor(get(urlEqualTo("/"))
.willReturn(aResponse()
.withStatus(HttpStatus.OK.value())
.withBody("Hello")));
assertEquals("Hello", google.request());
}
public static class RandomPortInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
// If the next statement is commented out,
// Feign will go to google.com instead of localhost
TestPropertySourceUtils
.addInlinedPropertiesToEnvironment(applicationContext,
"google.url=" + "http://localhost:" + wireMockRule.port()
);
}
}
}
Run Code Online (Sandbox Code Playgroud)
或者你可以尝试打System.setProperty()在@BeforeClass你的测试方法.
小智 5
过去,对微服务应用程序进行集成测试基本上有两种选择:
第一个选项的明显缺点是部署所有依赖项(其他服务、数据库等)也很麻烦。此外,它速度慢且难以调试。
第二种选择速度更快且麻烦更少,但由于可能的代码更改,很容易最终产生与实际行为不同的存根。因此,在部署到 prod 时,可能会有成功的测试但失败的应用程序。
更好的解决方案是使用消费者驱动的合同验证,以便您确保提供者服务的 API 与消费者调用兼容。为此,Spring 开发人员可以使用Spring Cloud Contract。对于其他环境,有一个名为PACT的框架。两者都可以与 Feign 客户端一起使用。这是 PACT 的示例。
Bas*_*igt -9
使用Spring的RestTemplate代替feign。RestTemplate 还能够通过 eureka 解析服务名称,因此您可以执行以下操作:
@Component
public class SomeService {
@Autowired
RestTemplate restTemplate;
public String doSomething() {
try {
restTemplate.postForEntity("http://my-service/some/url",
new BazzleRequest(...),
Void.class);
return "so bazzle my eyes dazzle";
} catch(HttpStatusCodeException e) {
return "Not bazzle today!";
}
}
}
Run Code Online (Sandbox Code Playgroud)
使用 Wiremock 比 feign 更容易测试。