Skip to content

Commit

Permalink
Merge pull request #171 from QA-Automation-Starter/159-mockserver-tut…
Browse files Browse the repository at this point in the history
…orial

159 mockserver tutorial
  • Loading branch information
adrian-herscu authored Aug 7, 2023
2 parents 23cbb9b + aecd3f3 commit 77ddfc4
Show file tree
Hide file tree
Showing 10 changed files with 303 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
@edu.umd.cs.findbugs.annotations.SuppressFBWarnings(
value = "BC_UNCONFIRMED_CAST_OF_RETURN_VALUE",
justification = "JGiven framework limitation")
abstract public class AbstractMockedServiceTest<T extends AnyScenarioType, GIVEN extends GenericFixtures<T, ?> & ScenarioType<T>, WHEN extends GenericActions<T, ?> & ScenarioType<T>, THEN extends GenericVerifications<T, ?> & ScenarioType<T>>
abstract public class AbstractWireMockTest<T extends AnyScenarioType, GIVEN extends GenericFixtures<T, ?> & ScenarioType<T>, WHEN extends GenericActions<T, ?> & ScenarioType<T>, THEN extends GenericVerifications<T, ?> & ScenarioType<T>>
extends
UnitilsScenarioTest<BaseConfiguration, T, GIVEN, WHEN, THEN> {

Expand All @@ -53,7 +53,7 @@ abstract public class AbstractMockedServiceTest<T extends AnyScenarioType, GIVEN
/**
* Initializes with {@link BaseConfiguration}.
*/
protected AbstractMockedServiceTest() {
protected AbstractWireMockTest() {
super(BaseConfiguration.class);
wireMockServer = wireMockServerOnDynamicPort();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,14 +99,6 @@ public class StageEx<SELF extends StageEx<?>> extends Stage<SELF> {
@ExpectedScenarioState
public CurrentStep currentStep;

@Override
@SneakyThrows
@Deprecated // the description no longer appears in report
public final SELF $(final String description,
@Hidden final StepFunction<? super SELF> function) {
return super.$(description, function);
}

protected final SELF alert(
final Consumer<Alert> action,
final WebDriver driver) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,7 @@
value = "BC_UNCONFIRMED_CAST_OF_RETURN_VALUE",
justification = "JGiven framework limitation")
@SuppressWarnings({ "boxing" })
public final class GenericRestTest extends
AbstractMockedServiceTest<RestScenarioType, RestFixtures<?>, RestActions<?>, RestVerifications<?>> {
public final class GenericRestTest extends AbstractWireMockTest<RestScenarioType, RestFixtures<?>, RestActions<?>, RestVerifications<?>> {

private Client client;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,7 @@
value = "BC_UNCONFIRMED_CAST_OF_RETURN_VALUE",
justification = "JGiven framework limitation")
@Slf4j
public final class GenericWebDriverTest extends
AbstractMockedServiceTest<WebDriverScenarioType, WebDriverFixtures<?>, WebDriverActions<?>, WebDriverVerifications<?>> {
public final class GenericWebDriverTest extends AbstractWireMockTest<WebDriverScenarioType, WebDriverFixtures<?>, WebDriverActions<?>, WebDriverVerifications<?>> {

private WebDriverEx driver;

Expand Down
5 changes: 5 additions & 0 deletions qa-testing-example/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,11 @@
<artifactId>qa-jgiven-webdriver</artifactId>
<version>0.0.28-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.mock-server</groupId>
<artifactId>mockserver-netty</artifactId>
<version>5.14.0</version>
</dependency>
<dependency>
<groupId>dev.aherscu.qa</groupId>
<artifactId>qa-jgiven-rest</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
/*
* Copyright 2023 Adrian Herscu
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package dev.aherscu.qa.testing.example;

import java.io.*;
import java.net.*;

import javax.ws.rs.core.*;

import org.mockserver.client.*;
import org.mockserver.integration.*;
import org.testng.annotations.*;

import dev.aherscu.qa.jgiven.commons.model.*;
import dev.aherscu.qa.jgiven.commons.steps.*;
import dev.aherscu.qa.jgiven.commons.utils.*;
import dev.aherscu.qa.testing.utils.config.*;
import lombok.*;
import lombok.extern.slf4j.*;

/**
* Contains REST sample tests just to ensure that the testing infrastructure
* works as required.
*
* @author aherscu
* @param <T>
* type of scenario
* @param <GIVEN>
* type of fixtures
* @param <WHEN>
* type of actions
* @param <THEN>
* type of verifications
*
*/
@edu.umd.cs.findbugs.annotations.SuppressFBWarnings(
value = "BC_UNCONFIRMED_CAST_OF_RETURN_VALUE",
justification = "JGiven framework limitation")
@Slf4j
abstract public class AbstractMockServerTest<T extends AnyScenarioType, GIVEN extends GenericFixtures<T, ?> & ScenarioType<T>, WHEN extends GenericActions<T, ?> & ScenarioType<T>, THEN extends GenericVerifications<T, ?> & ScenarioType<T>>
extends
UnitilsScenarioTest<BaseConfiguration, T, GIVEN, WHEN, THEN> {

public static final int DEFAULT_PORT = 1080;

protected final MockServerClient mockServer;

private final boolean usingOutOfProcessMockServer;

/**
* If port 1080 is free will initiate an in-process MockServer, otherwise
* will try connecting to port 1080.
*/
protected AbstractMockServerTest() {
super(BaseConfiguration.class);

usingOutOfProcessMockServer = canUseOutOfProcessMockServer();

log.debug("using out-of-process MockServer: {}",
usingOutOfProcessMockServer);

mockServer = usingOutOfProcessMockServer
? new MockServerClient("localhost", outOfProcessPort())
: ClientAndServer.startClientAndServer(0);
}

private boolean canUseOutOfProcessMockServer() {
try (val socket = new ServerSocket(outOfProcessPort())) {
socket.close();
return false;
} catch (final IOException ioe) {
return true;
}
}

protected int outOfProcessPort() {
return DEFAULT_PORT;
}

@AfterClass(alwaysRun = true)
protected void stopMockRestServer() {
if (!usingOutOfProcessMockServer)
mockServer.stop();
}

protected URI mockServerUri() {
return UriBuilder.fromUri("http://{host}:{port}")
.build("localhost", mockServer.getPort());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
/*
* Copyright 2023 Adrian Herscu
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package dev.aherscu.qa.testing.example.scenarios.tutorial6;

import static dev.aherscu.qa.testing.utils.StringUtilsExtensions.*;
import static java.util.Arrays.*;
import static javax.ws.rs.core.Response.Status.Family.*;
import static org.hamcrest.Matchers.*;
import static org.mockserver.matchers.Times.*;
import static org.mockserver.model.HttpError.*;
import static org.mockserver.model.HttpRequest.*;
import static org.mockserver.model.HttpResponse.*;
import static org.mockserver.model.MediaType.*;

import java.util.*;

import javax.ws.rs.client.*;

import org.mockserver.mock.*;
import org.mockserver.model.*;
import org.mockserver.verify.*;
import org.testng.annotations.*;

import dev.aherscu.qa.jgiven.commons.tags.*;
import dev.aherscu.qa.jgiven.rest.model.*;
import dev.aherscu.qa.jgiven.rest.steps.*;
import dev.aherscu.qa.jgiven.rest.tags.*;
import dev.aherscu.qa.testing.example.*;
import dev.aherscu.qa.testing.utils.assertions.*;
import dev.aherscu.qa.testing.utils.rest.*;
import lombok.*;

/**
* Contains REST sample tests just to ensure that the testing infrastructure
* works as required.
*
* @author aherscu
*
*/
@SelfTest
@RestTest
@edu.umd.cs.findbugs.annotations.SuppressFBWarnings(
value = "BC_UNCONFIRMED_CAST_OF_RETURN_VALUE",
justification = "JGiven framework limitation")
@SuppressWarnings({ "boxing" })
public final class GenericMockServerRestTest extends
AbstractMockServerTest<RestScenarioType, RestFixtures<?>, RestActions<?>, RestVerifications<?>> {

private Client client;

private Expectation[] expectations;

private static ExpectationId[] as(final Expectation[] expectations) {
return Arrays.stream(expectations)
.map(Expectation::getId)
.map(ExpectationId::expectationId)
.toArray(ExpectationId[]::new);
}

@Test
@Reference("159")
@SneakyThrows
public void shouldVerifyAccessByExpectionId() {
given()
.a_REST_client(client);

when()
.connecting_to(mockServerUri())
.and().appending_path("dummy")
.and().getting_the_response();

then()
.$("the dummy request was sent",
__ -> mockServer.verify(as(expectations)));
}

@Test
@Reference("159")
@SneakyThrows
public void shouldVerifyAccessByDefinition() {
given()
.a_REST_client(client);

when()
.connecting_to(mockServerUri())
.and().appending_path("some-id")
.and().getting_the_response();

then()
.the_response_contents(asList(
new JsonAssertion<>("$[0].id", greaterThan(0)),
new JsonAssertion<>("$[1].id", equalTo(2))))
.and().$("the some id request was sent",
__ -> mockServer.verify(request().withPath("/some-id")));
}

@Test
@Reference("159")
@SneakyThrows
public void shouldVerifyAccessedTwice() {
given()
.a_REST_client(client);

when()
.connecting_to(mockServerUri())
.and().appending_path("twice")
.and().getting_the_response()
.and().getting_the_response()
.and().getting_the_response(); // should return 404

then()
.the_response_status(is(CLIENT_ERROR))
.and().$("the method was called more than twice",
__ -> mockServer.verify(request().withPath("/twice"),
VerificationTimes.exactly(3)));
}

@Test
@Reference("159")
@SneakyThrows
public void shouldVerifyAccessOfDroppedConnection() {
given()
.a_REST_client(client);

when()
.connecting_to(mockServerUri())
.and().appending_path("drop-connection")
.and().$("trying to access",
self -> self.safely(__ -> self.getting_the_response()));

then()
.$("the drop connection request was sent",
__ -> mockServer.verify(request()
.withPath("/drop-connection")));
}

@AfterClass
private void afterClassCloseRestClient() {
client.close();
}

@BeforeClass
private void beforeClassAddExpectations() {
expectations = mockServer // GET is implied
.when(request().withPath("/dummy"))
.respond(response(EMPTY));

mockServer // GET is implied
.when(request().withPath("/twice"), exactly(2))
.respond(response(EMPTY));

mockServer // GET is implied
.when(request().withPath("/drop-connection"))
.error(error().withDropConnection(true));

mockServer // GET is implied
.when(request().withPath("/some-id"))
.respond(response()
.withBody("[{\"id\":1},{\"id\":2},{\"id\":3}]", JSON_UTF_8));
}

@BeforeClass
private void beforeClassOpenRestClient() {
client = LoggingClientBuilder.newClient();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,13 @@

import javax.ws.rs.client.*;

import dev.aherscu.qa.jgiven.rest.tags.*;
import org.testng.annotations.*;

import dev.aherscu.qa.jgiven.commons.*;
import dev.aherscu.qa.jgiven.commons.tags.*;
import dev.aherscu.qa.jgiven.rest.model.*;
import dev.aherscu.qa.jgiven.rest.steps.*;
import dev.aherscu.qa.jgiven.rest.tags.*;
import dev.aherscu.qa.testing.utils.assertions.*;
import dev.aherscu.qa.testing.utils.rest.*;

Expand All @@ -47,7 +47,7 @@
@SuppressWarnings({ "boxing" })
public final class GenericRestTest
extends
AbstractMockedServiceTest<RestScenarioType, RestFixtures<?>, RestActions<?>, RestVerifications<?>> {
AbstractWireMockTest<RestScenarioType, RestFixtures<?>, RestActions<?>, RestVerifications<?>> {

private Client client;

Expand Down Expand Up @@ -77,10 +77,13 @@ private void afterClassCloseRestClient() {
}

@BeforeClass
private void beforeClassOpenRestClient() {
private void beforeClassAddStubs() {
wireMockServer.stubFor(get(urlEqualTo("/some-id"))
.willReturn(ok("[{id:1},{id:2},{id:3}]")));
}

@BeforeClass
private void beforeClassOpenRestClient() {
client = LoggingClientBuilder.newClient();
}
}
Loading

0 comments on commit 77ddfc4

Please sign in to comment.