使用eureka和ribbon的测试服务
Test service that uses eureka and ribbon
我正在使用带有 netflix 堆栈和 spring 引导的微服务构建应用程序。让我烦恼的一件事是我还没有集成测试,我可以在其中模拟周围的服务。
所以,我有服务 A,它是一个带有功能区的 eureka 客户端,可以在调用期间将 eureka 名称解析为已注册服务 B 的 URL。
所以理想情况下我想用 spring boot 的 integrationtest 注释启动应用程序,使用 wiremock 模拟服务 B 然后调用服务 A 的方法,这应该调用我的模拟服务 B 使用服务的符号名称。
有人解决了吗?我已经搜索了已经这样做的人的博客条目等,但找不到任何...
我知道 SO 文章 但据我所知,这只是防止发现客户端抱怨。
一种选择是使用 Camel mock/replace Eureka 端点。应该有配置告诉您的应用程序在哪里寻找 Eureka,因此在您的测试配置中覆盖它以指向新端点。
然后使用码头或 http 在 test/src 中创建一个 Camel 路由来表示这个新端点,这将 return LoadBalancerClient 期望的响应。该响应将包含正在测试的 URI(即您的应用程序)。
public class MockRemoteEurekaServer extends ExternalResource {
public static final String EUREKA_API_BASE_PATH = "/eureka/v2/";
private final Map<String, Application> applicationMap;
private final Map<String, Application> applicationDeltaMap;
private final Server server;
private boolean sentDelta;
private int port;
private volatile boolean simulateNotReady;
public MockRemoteEurekaServer(int port, Map<String, Application> applicationMap,
Map<String, Application> applicationDeltaMap) {
this.applicationMap = applicationMap;
this.applicationDeltaMap = applicationDeltaMap;
ServletHandler handler = new AppsResourceHandler();
EurekaServerConfig serverConfig = new DefaultEurekaServerConfig();
EurekaServerContext serverContext = mock(EurekaServerContext.class);
when(serverContext.getServerConfig()).thenReturn(serverConfig);
handler.addFilterWithMapping(ServerRequestAuthFilter.class, "/*", 1).setFilter(new ServerRequestAuthFilter(serverContext));
handler.addFilterWithMapping(RateLimitingFilter.class, "/*", 1).setFilter(new RateLimitingFilter(serverContext));
server = new Server(port);
server.addHandler(handler);
System.out.println(String.format(
"Created eureka server mock with applications map %s and applications delta map %s",
stringifyAppMap(applicationMap), stringifyAppMap(applicationDeltaMap)));
}
@Override
protected void before() throws Throwable {
start();
}
@Override
protected void after() {
try {
stop();
} catch (Exception e) {
Assert.fail(e.getMessage());
}
}
public void start() throws Exception {
server.start();
port = server.getConnectors()[0].getLocalPort();
}
public void stop() throws Exception {
server.stop();
}
public boolean isSentDelta() {
return sentDelta;
}
public int getPort() {
return port;
}
public void simulateNotReady(boolean simulateNotReady) {
this.simulateNotReady = simulateNotReady;
}
private static String stringifyAppMap(Map<String, Application> applicationMap) {
StringBuilder builder = new StringBuilder();
for (Map.Entry<String, Application> entry : applicationMap.entrySet()) {
String entryAsString = String.format("{ name : %s , instance count: %d }", entry.getKey(),
entry.getValue().getInstances().size());
builder.append(entryAsString);
}
return builder.toString();
}
private class AppsResourceHandler extends ServletHandler {
@Override
public void handle(String target, HttpServletRequest request, HttpServletResponse response, int dispatch)
throws IOException, ServletException {
if (simulateNotReady) {
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
return;
}
String authName = request.getHeader(AbstractEurekaIdentity.AUTH_NAME_HEADER_KEY);
String authVersion = request.getHeader(AbstractEurekaIdentity.AUTH_VERSION_HEADER_KEY);
String authId = request.getHeader(AbstractEurekaIdentity.AUTH_ID_HEADER_KEY);
Assert.assertNotNull(authName);
Assert.assertNotNull(authVersion);
Assert.assertNotNull(authId);
Assert.assertTrue(!authName.equals(ServerRequestAuthFilter.UNKNOWN));
Assert.assertTrue(!authVersion.equals(ServerRequestAuthFilter.UNKNOWN));
Assert.assertTrue(!authId.equals(ServerRequestAuthFilter.UNKNOWN));
for (FilterHolder filterHolder : this.getFilters()) {
filterHolder.getFilter().doFilter(request, response, new FilterChain() {
@Override
public void doFilter(ServletRequest request, ServletResponse response)
throws IOException, ServletException {
// do nothing;
}
});
}
String pathInfo = request.getPathInfo();
System.out.println(
"Eureka resource mock, received request on path: " + pathInfo + ". HTTP method: |" + request
.getMethod() + '|');
boolean handled = false;
if (null != pathInfo && pathInfo.startsWith("")) {
pathInfo = pathInfo.substring(EUREKA_API_BASE_PATH.length());
if (pathInfo.startsWith("apps/delta")) {
Applications apps = new Applications();
for (Application application : applicationDeltaMap.values()) {
apps.addApplication(application);
}
apps.setAppsHashCode(apps.getReconcileHashCode());
sendOkResponseWithContent((Request) request, response, toJson(apps));
handled = true;
sentDelta = true;
} else if (pathInfo.startsWith("apps")) {
Applications apps = new Applications();
for (Application application : applicationMap.values()) {
apps.addApplication(application);
}
apps.setAppsHashCode(apps.getReconcileHashCode());
sendOkResponseWithContent((Request) request, response, toJson(apps));
handled = true;
}
}
if (!handled) {
response.sendError(HttpServletResponse.SC_NOT_FOUND,
"Request path: " + pathInfo + " not supported by eureka resource mock.");
}
}
private void sendOkResponseWithContent(Request request, HttpServletResponse response, String content)
throws IOException {
response.setContentType("application/json; charset=UTF-8");
response.setStatus(HttpServletResponse.SC_OK);
response.getOutputStream().write(content.getBytes("UTF-8"));
response.getOutputStream().flush();
request.setHandled(true);
System.out.println("Eureka resource mock, sent response for request path: " + request.getPathInfo() +
" with content" + content);
}
}
private String toJson(Applications apps) throws IOException {
return new EurekaJsonJacksonCodec().getObjectMapper(Applications.class).writeValueAsString(apps);
}
}
我正在使用带有 netflix 堆栈和 spring 引导的微服务构建应用程序。让我烦恼的一件事是我还没有集成测试,我可以在其中模拟周围的服务。
所以,我有服务 A,它是一个带有功能区的 eureka 客户端,可以在调用期间将 eureka 名称解析为已注册服务 B 的 URL。
所以理想情况下我想用 spring boot 的 integrationtest 注释启动应用程序,使用 wiremock 模拟服务 B 然后调用服务 A 的方法,这应该调用我的模拟服务 B 使用服务的符号名称。
有人解决了吗?我已经搜索了已经这样做的人的博客条目等,但找不到任何...
我知道 SO 文章
一种选择是使用 Camel mock/replace Eureka 端点。应该有配置告诉您的应用程序在哪里寻找 Eureka,因此在您的测试配置中覆盖它以指向新端点。
然后使用码头或 http 在 test/src 中创建一个 Camel 路由来表示这个新端点,这将 return LoadBalancerClient 期望的响应。该响应将包含正在测试的 URI(即您的应用程序)。
public class MockRemoteEurekaServer extends ExternalResource {
public static final String EUREKA_API_BASE_PATH = "/eureka/v2/";
private final Map<String, Application> applicationMap;
private final Map<String, Application> applicationDeltaMap;
private final Server server;
private boolean sentDelta;
private int port;
private volatile boolean simulateNotReady;
public MockRemoteEurekaServer(int port, Map<String, Application> applicationMap,
Map<String, Application> applicationDeltaMap) {
this.applicationMap = applicationMap;
this.applicationDeltaMap = applicationDeltaMap;
ServletHandler handler = new AppsResourceHandler();
EurekaServerConfig serverConfig = new DefaultEurekaServerConfig();
EurekaServerContext serverContext = mock(EurekaServerContext.class);
when(serverContext.getServerConfig()).thenReturn(serverConfig);
handler.addFilterWithMapping(ServerRequestAuthFilter.class, "/*", 1).setFilter(new ServerRequestAuthFilter(serverContext));
handler.addFilterWithMapping(RateLimitingFilter.class, "/*", 1).setFilter(new RateLimitingFilter(serverContext));
server = new Server(port);
server.addHandler(handler);
System.out.println(String.format(
"Created eureka server mock with applications map %s and applications delta map %s",
stringifyAppMap(applicationMap), stringifyAppMap(applicationDeltaMap)));
}
@Override
protected void before() throws Throwable {
start();
}
@Override
protected void after() {
try {
stop();
} catch (Exception e) {
Assert.fail(e.getMessage());
}
}
public void start() throws Exception {
server.start();
port = server.getConnectors()[0].getLocalPort();
}
public void stop() throws Exception {
server.stop();
}
public boolean isSentDelta() {
return sentDelta;
}
public int getPort() {
return port;
}
public void simulateNotReady(boolean simulateNotReady) {
this.simulateNotReady = simulateNotReady;
}
private static String stringifyAppMap(Map<String, Application> applicationMap) {
StringBuilder builder = new StringBuilder();
for (Map.Entry<String, Application> entry : applicationMap.entrySet()) {
String entryAsString = String.format("{ name : %s , instance count: %d }", entry.getKey(),
entry.getValue().getInstances().size());
builder.append(entryAsString);
}
return builder.toString();
}
private class AppsResourceHandler extends ServletHandler {
@Override
public void handle(String target, HttpServletRequest request, HttpServletResponse response, int dispatch)
throws IOException, ServletException {
if (simulateNotReady) {
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
return;
}
String authName = request.getHeader(AbstractEurekaIdentity.AUTH_NAME_HEADER_KEY);
String authVersion = request.getHeader(AbstractEurekaIdentity.AUTH_VERSION_HEADER_KEY);
String authId = request.getHeader(AbstractEurekaIdentity.AUTH_ID_HEADER_KEY);
Assert.assertNotNull(authName);
Assert.assertNotNull(authVersion);
Assert.assertNotNull(authId);
Assert.assertTrue(!authName.equals(ServerRequestAuthFilter.UNKNOWN));
Assert.assertTrue(!authVersion.equals(ServerRequestAuthFilter.UNKNOWN));
Assert.assertTrue(!authId.equals(ServerRequestAuthFilter.UNKNOWN));
for (FilterHolder filterHolder : this.getFilters()) {
filterHolder.getFilter().doFilter(request, response, new FilterChain() {
@Override
public void doFilter(ServletRequest request, ServletResponse response)
throws IOException, ServletException {
// do nothing;
}
});
}
String pathInfo = request.getPathInfo();
System.out.println(
"Eureka resource mock, received request on path: " + pathInfo + ". HTTP method: |" + request
.getMethod() + '|');
boolean handled = false;
if (null != pathInfo && pathInfo.startsWith("")) {
pathInfo = pathInfo.substring(EUREKA_API_BASE_PATH.length());
if (pathInfo.startsWith("apps/delta")) {
Applications apps = new Applications();
for (Application application : applicationDeltaMap.values()) {
apps.addApplication(application);
}
apps.setAppsHashCode(apps.getReconcileHashCode());
sendOkResponseWithContent((Request) request, response, toJson(apps));
handled = true;
sentDelta = true;
} else if (pathInfo.startsWith("apps")) {
Applications apps = new Applications();
for (Application application : applicationMap.values()) {
apps.addApplication(application);
}
apps.setAppsHashCode(apps.getReconcileHashCode());
sendOkResponseWithContent((Request) request, response, toJson(apps));
handled = true;
}
}
if (!handled) {
response.sendError(HttpServletResponse.SC_NOT_FOUND,
"Request path: " + pathInfo + " not supported by eureka resource mock.");
}
}
private void sendOkResponseWithContent(Request request, HttpServletResponse response, String content)
throws IOException {
response.setContentType("application/json; charset=UTF-8");
response.setStatus(HttpServletResponse.SC_OK);
response.getOutputStream().write(content.getBytes("UTF-8"));
response.getOutputStream().flush();
request.setHandled(true);
System.out.println("Eureka resource mock, sent response for request path: " + request.getPathInfo() +
" with content" + content);
}
}
private String toJson(Applications apps) throws IOException {
return new EurekaJsonJacksonCodec().getObjectMapper(Applications.class).writeValueAsString(apps);
}
}