유레카와 리본을 이용한 테스트 서비스
저는 넷플릭스 스택과 스프링 부트로 마이크로 서비스를 이용하여 애플리케이션을 구축하고 있습니다.한 가지 궁금한 점은 주변 서비스를 조롱할 수 있는 통합 테스트가 아직 없다는 것입니다.
그래서 저는 통화 중에 등록된 B 서비스의 URL로 eureka 이름을 해결하기 위해 리본이 달린 eureka 클라이언트인 A 서비스를 가지고 있습니다.
따라서 이상적으로 저는 스프링 부트의 통합 테스트 주석으로 애플리케이션을 시작하고, 와이어모크를 사용하여 서비스 B를 시뮬레이션한 다음 서비스 A의 메소드를 호출하고, 이것은 서비스의 상징적인 이름을 사용하여 제 조롱된 서비스 B를 호출해야 합니다.
이미 해결한 사람이 있나요?이런 사람들의 블로그 엔트리 등을 이미 검색했지만 찾을 수 없었습니다...
나는 모칸 유레카 페이그 클라이언트 for Unit testing이라는 SO 기사를 알고 있지만, 내가 보기에는 이것은 발견 클라이언트가 불평하는 것을 막습니다.
다음 코드(https://github.com/Netflix/eureka/blob/a7a8d278e6399bbff5faa49b9fcbcd7ea9e854f4/eureka-core/src/test/java/com/netflix/eureka/mock/MockRemoteEurekaServer.java) 에서 제공하는 코드가 도움이 될 수 있습니다.
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);
}
}
한 가지 옵션은 Camel을 사용하여 Eureka 끝점을 모의/교체하는 것입니다.앱에서 Eureka를 찾을 위치를 알려주는 구성이 있을 것이므로 테스트 구성에서 해당 구성을 재정의하여 새 끝점을 가리킵니다.
그런 다음 jetty 또는 http를 사용하여 test/src에서 Camel 경로를 만들어 이 새 끝점을 나타내면 LoadBalancer Client가 예상하는 응답이 반환됩니다.이 응답에는 URI가 테스트 대상이 됩니다(즉, 응용 프로그램).
언급URL : https://stackoverflow.com/questions/36148544/test-service-that-uses-eureka-and-ribbon
'programing' 카테고리의 다른 글
트리거에서 DELMITER //의 역할은 무엇입니까? (0) | 2023.07.23 |
---|---|
JMH를 통한 스프링 부트 애플리케이션 벤치마킹 (0) | 2023.07.23 |
C에서 FILE 키워드는 정확히 무엇입니까? (0) | 2023.07.23 |
GETDATE()가 잘못된 식별자인 이유는 무엇입니까? (0) | 2023.07.23 |
YAML 파일은 들여쓰기로 탭을 포함할 수 없습니다. (0) | 2023.07.23 |