WireMock yerine bir diğer seçenek MockServer
Açıklaması şöyle
... sets up a mock server locally and allows to easily define requests and responses.
Açıklaması şöyle
WireMock is a great API mocking library. It is written in Java and used mostly by Java developers. But because it relies on easy to use configuration, it can be used by anyone who has to mock API.WireMock allows you not also to stub responses by endpoint URL, you can also put it between the client and your existing API. With proxying some of the requests can be still handled by your API server, while others are mocked by WireMock.
Şu satırı dahil ederiz
import com.github.tomakehurst.wiremock.WireMockServerimport com.github.tomakehurst.wiremock.client.WireMock.*import com.github.tomakehurst.wiremock.core.WireMockConfiguration
Eğer testlerde bir tane gerçek bean yerine test bean kullanmak istersek şöyle yaparız
spring:main:allow-bean-definition-overriding: true
Maven
Örnek
Şöyle yaparız
<dependency> <groupId>com.github.tomakehurst</groupId> <artifactId>wiremock</artifactId> <version>2.27.2</version> <scope>test</scope> </dependency>
Örnek - Fat Jar
Bu kütüthanenin groupId değeri com.github.tomakehurst yerine artık org.wiremock oldu. Şöyle yaparız
<dependency> <groupId>org.wiremock</groupId> <artifactId>wiremock-standalone</artifactId> <version>2.27.2</version> <scope>test</scope> </dependency>
Örnek - Java 8 Çok Eski Kullanmayın
Şöyle yaparız
<dependency> <groupId>com.github.tomakehurst</groupId> <artifactId>wiremock-jre8</artifactId> <version>2.28.1</version> <scope>test</scope> </dependency>
Gradle
Şöyle yaparız
dependencies { testImplementation 'com.github.tomakehurst:wiremock:2.27.2' }
Kullanım
WireMockServer nesnesi yaratmak gerekir. 1. Bu nesneyi sarmalayan bir başka sınıf yaratılarak tüm testlere alt yapı oluşturulabilir
2. Junit 5 Extension kullanılabilir
Örnek - Record Playback İle Kullanım
Açıklaması şöyle
WireMock has a feature called Record and Playback. It can create stub mappings automatically from requests it has received. Combined with its proxying feature this allows us to “record” stub mappings from interaction with existing APIs. When in action, we have the option to either call the external API and capture the response or use one of the earlier recorded stubs to reply to the caller without calling the external API.
Şu satırı dahil ederiz
import com.github.tomakehurst.wiremock.WireMockServer; import com.github.tomakehurst.wiremock.client.WireMock; import com.github.tomakehurst.wiremock.common.ConsoleNotifier; import com.github.tomakehurst.wiremock.core.WireMockConfiguration; import com.github.tomakehurst.wiremock.matching.RequestPatternBuilder; import com.github.tomakehurst.wiremock.recording.RecordSpec; import com.github.tomakehurst.wiremock.recording.RecordingStatus;
Örnek
Elimizde şöyle bir konfigürasyon olsun. Bunu okuyan kendi kodumuz var.
wiremock-config: proxies: - name: university port: 9081 url: http://universities.hipolabs.com recording: true universitiesBaseURL: http://localhost:9081
Şöyle yaparız. Burada önce gerçek cevap kaydediliyor.
@ActiveProfiles(value = "integration") @SpringBootTest(classes = SpringBootWiremockApplication.class, webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT) @Slf4j @AutoConfigureMockMvc class SpringBootWiremockApplicationTests { @Autowired private MockMvc mockMvc; @Autowired private WireMockConfig wireMockConfig; private final List<WireMockServer> servers = new ArrayList<>(); Function<WireMockProxy, WireMockServer> getMockServer = (WireMockProxy proxy) -> new WireMockServer( WireMockConfiguration.options() .port(proxy.getPort()) .notifier(new ConsoleNotifier(true))); @BeforeEach void startRecording() { List<WireMockProxy> proxies = wireMockConfig.getProxies(); if (!CollectionUtils.isEmpty(proxies)) { for (WireMockProxy proxy : proxies) { WireMockServer wireMockServer = getMockServer.apply(proxy); wireMockServer.start(); if (proxy.isRecording()) { wireMockServer.startRecording(config(proxy.getUrl(), true)); } servers.add(wireMockServer); } } } @AfterEach void stopRecording() { if (!CollectionUtils.isEmpty(servers)) { for (WireMockServer server : servers) { if (server.getRecordingStatus().getStatus().equals(RecordingStatus.Recording)) { server.stopRecording(); } server.stop(); } } } private RecordSpec config(String recordingURL, boolean recordingEnabled) { return WireMock.recordSpec() .forTarget(recordingURL) .onlyRequestsMatching(RequestPatternBuilder.allRequests()) .captureHeader("Accept") .makeStubsPersistent(recordingEnabled) .ignoreRepeatRequests() .matchRequestBodyWithEqualToJson(true, true) .build(); } }
Örnek - Unit Test İle Kullanım
Test başında @BeforeEach veya @BeforeAll ile sunucuyu başlatırız. Şöyle yaparız
@BeforeEach void setUp() { server = new WireMockServer(WireMockConfiguration.wireMockConfig().dynamicPort()); server.start(); }
Test sonunda @AfterEach veya @AfterAll ile sunucuyu kapatırız. Şöyle yaparız
@AfterEach void tearDown() { server.shutdownServer(); }
Örnek - JUnit 5 Extension
Şöyle bir JUnit 5 extension olsun
import org.junit.jupiter.api.extension.AfterAllCallback; import org.junit.jupiter.api.extension.AfterEachCallback; import org.junit.jupiter.api.extension.ExtensionContext; import org.junit.jupiter.api.extension.ParameterContext; import org.junit.jupiter.api.extension.ParameterResolutionException; import org.junit.jupiter.api.extension.ParameterResolver; import com.github.tomakehurst.wiremock.WireMockServer; import com.github.tomakehurst.wiremock.common.ConsoleNotifier; public class WireMockExtension implements AfterAllCallback, AfterEachCallback, ParameterResolver { private WireMockServer wiremockServer; @Override public void afterAll(ExtensionContext context) { wiremockServer.shutdown(); } @Override public void afterEach(ExtensionContext context) { wiremockServer.resetAll(); } @Override public WireMockServer resolveParameter(ParameterContext parameterContext, ExtensionContext arg1) throws ParameterResolutionException { wiremockServer = new WireMockServer(options() .notifier(new ConsoleNotifier(true)) .dynamicPort()); wiremockServer.start(); return wiremockServer; } @Override public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext context) throws ParameterResolutionException { return parameterContext.getParameter().getType().equals(WireMockServer.class) && parameterContext.isAnnotated(WireMockInstance.class); } }
Şöyle bir anotasyonumuz olsun
import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.PARAMETER) public @interface WireMockInstance { }
Test sınıfının iskeleti şöyle olsun
@ExtendWith({WireMockExtension.class, SpringExtension.class}) @SpringBootTest( webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, properties = { "logging.level.root=INFO", "zephyr.api.username=zephyrTestUser", "zephyr.api.password=zephyrTestPassword", "slack.app.oauth.accessToken=testFakeToken" }) @ContextConfiguration(initializers = AppIntegrationTestPropertyInitializer.class) public class IntegrationTest { } //Because WireMock servers start on random ports, we need to set up our Jira //and Slack API base URLs on the fly. //This is done using initializers in @ContextConfiguration import org.springframework.context.ApplicationContextInitializer; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.test.context.support.TestPropertySourceUtils; class AppIntegrationTestPropertyInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> { @Override public void initialize(ConfigurableApplicationContext applicationContext) { TestPropertySourceUtils.addInlinedPropertiesToEnvironment(applicationContext, "zephyr.api.baseUrl=" + String.format("%s/jira/rest/zapi/latest", IntegrationTest.zephyrWiremockServer.baseUrl())); TestPropertySourceUtils.addInlinedPropertiesToEnvironment(applicationContext, "slack.baseUrl=" + String.format("%s/api", IntegrationTest.slackWiremockServer.baseUrl())); } }
BeforeAll ve AfterEach şöyle olsun
@BeforeAll public static void beforeAll(@WireMockInstance WireMockServer server1, @WireMockInstance WireMockServer server2) { zephyrWiremockServer = server1; slackWiremockServer = server2; zephyrWiremockServer.addMockServiceRequestListener(new RequestListener() { Override public void requestReceived(Request request, Response response) { // if we would fail directly here in listener, JUnit error would be really hard to understand (as no response would be generated) // therefore saving the value to assert it later in main test flow isZephyrAuthSingleValued = request.getHeaders().getHeader("Authorization").isSingleValued(); } }); slackWiremockServer.addMockServiceRequestListener(new RequestListener() { @Override public void requestReceived(Request request, Response response) { // if we would fail directly here in listener, JUnit error would be really hard to understand (as no response would be generated) // therefore saving the value to assert it later in main test flow isSlackAuthSingleValued = request.getHeaders().getHeader("Authorization").isSingleValued(); } }); } @AfterEach public void afterEach() { assertTrue(isZephyrAuthSingleValued, "There must be only one 'Authorization' header in all Zephyr requests"); assertTrue(isSlackAuthSingleValued, "There must be only one 'Authorization' header in all Slack requests"); zephyrWiremockServer.verify(anyRequestedFor(anyUrl()) .withBasicAuth(new BasicCredentials("zephyrTestUser", "zephyrTestPassword"))); slackWiremockServer.verify(anyRequestedFor(anyUrl()) .withHeader("Authorization", equalTo("Bearer testFakeToken"))); }
Test şöyledir
@Test public void testAuthenticatedCallToJiraAndSlack() throws IOException { // mock Zephyr API response: String zephyrResponse = getResourceFileContent("IntegrationTest/testAuthenticatedCallToJiraAndSlack/zephyr_list_executions.json"); zephyrWiremockServer.stubFor(get(urlEqualTo("/jira/rest/zapi/latest/execution?issueId=5112096")) willReturn(aResponse() .withStatus(200) .withHeader("Content-Type", "application/json;charset=utf-8") .withBody(zephyrResponse))); // mock Slack API response: String slackResponse = getResourceFileContent("IntegrationTest/testAuthenticatedCallToJiraAndSlack/slack_response.json"); slackWiremockServer.stubFor(post(urlEqualTo("/api/chat.postMessage")) .withRequestBody(matchingJsonPath("$[?(@.channel == 'testing-channel')]")) .willReturn(aResponse() .withStatus(200) .withHeader("Content-Type", "application/json;charset=utf-8") .withBody(slackResponse))); // test String result = controller.getResource(); // verify assertEquals("OK result", result); } private String getResourceFileContent(String resourceName) throws IOException { URL url = Resources.getResource(resourceName); return Resources.toString(url, StandardCharsets.UTF_8); }
WireMock API
WireMockServer Sınıfı
WireMockServer Sınıfı yazısına taşıdım
Hiç yorum yok:
Yorum Gönder