From 8bc1fece21e76aef6c8778dd344eb61f0d405663 Mon Sep 17 00:00:00 2001 From: Adrian Herscu Date: Thu, 3 Aug 2023 13:58:25 +0300 Subject: [PATCH 01/13] initial implementation --- ...iceTest.java => AbstractWireMockTest.java} | 4 +- .../rest/scenarios/GenericRestTest.java | 3 +- .../scenarios/GenericWebDriverTest.java | 3 +- qa-testing-example/pom.xml | 5 + .../example/AbstractMockServerTest.java | 63 +++++++++++++ .../tutorial6/GenericMockServerRestTest.java | 93 +++++++++++++++++++ .../scenarios/tutorial6/GenericRestTest.java | 3 +- .../scenarios/tutorial6/StatefulRestTest.java | 3 +- .../src/test/resources/logback-common.xml | 3 + 9 files changed, 170 insertions(+), 10 deletions(-) rename qa-jgiven-commons/src/main/java/dev/aherscu/qa/jgiven/commons/{AbstractMockedServiceTest.java => AbstractWireMockTest.java} (87%) create mode 100644 qa-testing-example/src/main/java/dev/aherscu/qa/testing/example/AbstractMockServerTest.java create mode 100644 qa-testing-example/src/test/java/dev/aherscu/qa/testing/example/scenarios/tutorial6/GenericMockServerRestTest.java diff --git a/qa-jgiven-commons/src/main/java/dev/aherscu/qa/jgiven/commons/AbstractMockedServiceTest.java b/qa-jgiven-commons/src/main/java/dev/aherscu/qa/jgiven/commons/AbstractWireMockTest.java similarity index 87% rename from qa-jgiven-commons/src/main/java/dev/aherscu/qa/jgiven/commons/AbstractMockedServiceTest.java rename to qa-jgiven-commons/src/main/java/dev/aherscu/qa/jgiven/commons/AbstractWireMockTest.java index 0cd0806d1b..fec6481dbc 100644 --- a/qa-jgiven-commons/src/main/java/dev/aherscu/qa/jgiven/commons/AbstractMockedServiceTest.java +++ b/qa-jgiven-commons/src/main/java/dev/aherscu/qa/jgiven/commons/AbstractWireMockTest.java @@ -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 & ScenarioType, WHEN extends GenericActions & ScenarioType, THEN extends GenericVerifications & ScenarioType> +abstract public class AbstractWireMockTest & ScenarioType, WHEN extends GenericActions & ScenarioType, THEN extends GenericVerifications & ScenarioType> extends UnitilsScenarioTest { @@ -53,7 +53,7 @@ abstract public class AbstractMockedServiceTest, RestActions, RestVerifications> { +public final class GenericRestTest extends AbstractWireMockTest, RestActions, RestVerifications> { private Client client; diff --git a/qa-jgiven-webdriver/src/test/java/dev/aherscu/qa/jgiven/webdriver/scenarios/GenericWebDriverTest.java b/qa-jgiven-webdriver/src/test/java/dev/aherscu/qa/jgiven/webdriver/scenarios/GenericWebDriverTest.java index 8b9e40e30d..ac81b3f46c 100644 --- a/qa-jgiven-webdriver/src/test/java/dev/aherscu/qa/jgiven/webdriver/scenarios/GenericWebDriverTest.java +++ b/qa-jgiven-webdriver/src/test/java/dev/aherscu/qa/jgiven/webdriver/scenarios/GenericWebDriverTest.java @@ -44,8 +44,7 @@ value = "BC_UNCONFIRMED_CAST_OF_RETURN_VALUE", justification = "JGiven framework limitation") @Slf4j -public final class GenericWebDriverTest extends - AbstractMockedServiceTest, WebDriverActions, WebDriverVerifications> { +public final class GenericWebDriverTest extends AbstractWireMockTest, WebDriverActions, WebDriverVerifications> { private WebDriverEx driver; diff --git a/qa-testing-example/pom.xml b/qa-testing-example/pom.xml index b764077956..2faf1adad9 100644 --- a/qa-testing-example/pom.xml +++ b/qa-testing-example/pom.xml @@ -45,6 +45,11 @@ qa-jgiven-webdriver 0.0.28-SNAPSHOT + + org.mock-server + mockserver-netty + 5.14.0 + dev.aherscu.qa qa-jgiven-rest diff --git a/qa-testing-example/src/main/java/dev/aherscu/qa/testing/example/AbstractMockServerTest.java b/qa-testing-example/src/main/java/dev/aherscu/qa/testing/example/AbstractMockServerTest.java new file mode 100644 index 0000000000..8aee44e05e --- /dev/null +++ b/qa-testing-example/src/main/java/dev/aherscu/qa/testing/example/AbstractMockServerTest.java @@ -0,0 +1,63 @@ +/* + * 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 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.*; + +/** + * Contains REST sample tests just to ensure that the testing infrastructure + * works as required. + * + * @author aherscu + * @param + * type of scenario + * @param + * type of fixtures + * @param + * type of actions + * @param + * type of verifications + * + */ +@edu.umd.cs.findbugs.annotations.SuppressFBWarnings( + value = "BC_UNCONFIRMED_CAST_OF_RETURN_VALUE", + justification = "JGiven framework limitation") +abstract public class AbstractMockServerTest & ScenarioType, WHEN extends GenericActions & ScenarioType, THEN extends GenericVerifications & ScenarioType> + extends + UnitilsScenarioTest { + + protected final ClientAndServer mockServer; + + /** + * Initializes with {@link BaseConfiguration}. + */ + protected AbstractMockServerTest() { + super(BaseConfiguration.class); + // ISSUE no support for dynamically allocated port + mockServer = ClientAndServer.startClientAndServer(1080); + } + + @AfterClass(alwaysRun = true) + protected void stopMockRestServer() { + mockServer.stop(); + } +} diff --git a/qa-testing-example/src/test/java/dev/aherscu/qa/testing/example/scenarios/tutorial6/GenericMockServerRestTest.java b/qa-testing-example/src/test/java/dev/aherscu/qa/testing/example/scenarios/tutorial6/GenericMockServerRestTest.java new file mode 100644 index 0000000000..675e493b12 --- /dev/null +++ b/qa-testing-example/src/test/java/dev/aherscu/qa/testing/example/scenarios/tutorial6/GenericMockServerRestTest.java @@ -0,0 +1,93 @@ +/* + * 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 java.util.Arrays.*; +import static org.hamcrest.Matchers.*; +import static org.mockserver.model.HttpRequest.*; +import static org.mockserver.model.HttpResponse.*; + +import java.net.*; + +import javax.ws.rs.client.*; + +import org.mockserver.model.*; +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.*; + +/** + * 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, RestActions, RestVerifications> { + + private Client client; + + /** + * Should retrieve a JSON field from a REST service + */ + @Test + @Reference("159") + public void shouldRetrieveJsonFieldFromFakeRestService() { + given() + .a_REST_client(client); + + when() + .connecting_to( + URI.create("http://localhost:" + mockServer.getPort())) + .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)))); + } + + @AfterClass + private void afterClassCloseRestClient() { + client.close(); + } + + @BeforeClass + private void beforeClassOpenRestClient() { + mockServer + .when(request() + .withMethod("GET").withPath("/some-id")) + .respond(response() + .withBody("[{id:1},{id:2},{id:3}]", + MediaType.JSON_UTF_8)); + + client = LoggingClientBuilder.newClient(); + } +} diff --git a/qa-testing-example/src/test/java/dev/aherscu/qa/testing/example/scenarios/tutorial6/GenericRestTest.java b/qa-testing-example/src/test/java/dev/aherscu/qa/testing/example/scenarios/tutorial6/GenericRestTest.java index 2f2108d396..0b9fd1798c 100644 --- a/qa-testing-example/src/test/java/dev/aherscu/qa/testing/example/scenarios/tutorial6/GenericRestTest.java +++ b/qa-testing-example/src/test/java/dev/aherscu/qa/testing/example/scenarios/tutorial6/GenericRestTest.java @@ -46,8 +46,7 @@ justification = "JGiven framework limitation") @SuppressWarnings({ "boxing" }) public final class GenericRestTest - extends - AbstractMockedServiceTest, RestActions, RestVerifications> { + extends AbstractWireMockTest, RestActions, RestVerifications> { private Client client; diff --git a/qa-testing-example/src/test/java/dev/aherscu/qa/testing/example/scenarios/tutorial6/StatefulRestTest.java b/qa-testing-example/src/test/java/dev/aherscu/qa/testing/example/scenarios/tutorial6/StatefulRestTest.java index d84eabf2d9..8cb4ecd890 100644 --- a/qa-testing-example/src/test/java/dev/aherscu/qa/testing/example/scenarios/tutorial6/StatefulRestTest.java +++ b/qa-testing-example/src/test/java/dev/aherscu/qa/testing/example/scenarios/tutorial6/StatefulRestTest.java @@ -49,8 +49,7 @@ justification = "JGiven framework limitation") @SuppressWarnings({ "boxing" }) public final class StatefulRestTest - extends - AbstractMockedServiceTest, RestActions, RestVerifications> { + extends AbstractWireMockTest, RestActions, RestVerifications> { private Client client; diff --git a/qa-testing-example/src/test/resources/logback-common.xml b/qa-testing-example/src/test/resources/logback-common.xml index b8b4bb0cd8..35aed2cb19 100644 --- a/qa-testing-example/src/test/resources/logback-common.xml +++ b/qa-testing-example/src/test/resources/logback-common.xml @@ -71,6 +71,9 @@ + + + From 7aa68cf0c8040ef0188d1345a4a5e3111d457330 Mon Sep 17 00:00:00 2001 From: Adrian Herscu Date: Thu, 3 Aug 2023 14:50:11 +0300 Subject: [PATCH 02/13] removed irrelevant code --- qa-testing-example/src/test/resources/logback-common.xml | 2 -- 1 file changed, 2 deletions(-) diff --git a/qa-testing-example/src/test/resources/logback-common.xml b/qa-testing-example/src/test/resources/logback-common.xml index 35aed2cb19..46a22b52f3 100644 --- a/qa-testing-example/src/test/resources/logback-common.xml +++ b/qa-testing-example/src/test/resources/logback-common.xml @@ -72,8 +72,6 @@ in order to see interpolated properties --> - - From 8d24e467cd70bc3b62eb2a1c78f7b9698c25551f Mon Sep 17 00:00:00 2001 From: Adrian Herscu Date: Thu, 3 Aug 2023 14:50:37 +0300 Subject: [PATCH 03/13] added mockserver uri --- .../qa/testing/example/AbstractMockServerTest.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/qa-testing-example/src/main/java/dev/aherscu/qa/testing/example/AbstractMockServerTest.java b/qa-testing-example/src/main/java/dev/aherscu/qa/testing/example/AbstractMockServerTest.java index 8aee44e05e..d1a5a5752c 100644 --- a/qa-testing-example/src/main/java/dev/aherscu/qa/testing/example/AbstractMockServerTest.java +++ b/qa-testing-example/src/main/java/dev/aherscu/qa/testing/example/AbstractMockServerTest.java @@ -15,6 +15,10 @@ */ package dev.aherscu.qa.testing.example; +import java.net.*; + +import javax.ws.rs.core.*; + import org.mockserver.integration.*; import org.testng.annotations.*; @@ -60,4 +64,9 @@ protected AbstractMockServerTest() { protected void stopMockRestServer() { mockServer.stop(); } + + protected URI mockServerUri() { + return UriBuilder.fromUri("http://{host}:{port}") + .build("localhost", mockServer.getPort()); + } } From 2861a4967885336743ba60b83b7d1164e68c5935 Mon Sep 17 00:00:00 2001 From: Adrian Herscu Date: Thu, 3 Aug 2023 14:51:10 +0300 Subject: [PATCH 04/13] simplified code --- .../tutorial6/GenericMockServerRestTest.java | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/qa-testing-example/src/test/java/dev/aherscu/qa/testing/example/scenarios/tutorial6/GenericMockServerRestTest.java b/qa-testing-example/src/test/java/dev/aherscu/qa/testing/example/scenarios/tutorial6/GenericMockServerRestTest.java index 675e493b12..d66683ecd2 100644 --- a/qa-testing-example/src/test/java/dev/aherscu/qa/testing/example/scenarios/tutorial6/GenericMockServerRestTest.java +++ b/qa-testing-example/src/test/java/dev/aherscu/qa/testing/example/scenarios/tutorial6/GenericMockServerRestTest.java @@ -19,12 +19,10 @@ import static org.hamcrest.Matchers.*; import static org.mockserver.model.HttpRequest.*; import static org.mockserver.model.HttpResponse.*; - -import java.net.*; +import static org.mockserver.model.MediaType.*; import javax.ws.rs.client.*; -import org.mockserver.model.*; import org.testng.annotations.*; import dev.aherscu.qa.jgiven.commons.tags.*; @@ -34,6 +32,7 @@ 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 @@ -58,13 +57,13 @@ public final class GenericMockServerRestTest extends */ @Test @Reference("159") + @SneakyThrows public void shouldRetrieveJsonFieldFromFakeRestService() { given() .a_REST_client(client); when() - .connecting_to( - URI.create("http://localhost:" + mockServer.getPort())) + .connecting_to(mockServerUri()) .and().appending_path("some-id") .and().getting_the_response(); @@ -82,11 +81,10 @@ private void afterClassCloseRestClient() { @BeforeClass private void beforeClassOpenRestClient() { mockServer - .when(request() - .withMethod("GET").withPath("/some-id")) + .when(request() // GET is implied + .withPath("/some-id")) .respond(response() - .withBody("[{id:1},{id:2},{id:3}]", - MediaType.JSON_UTF_8)); + .withBody("[{\"id\":1},{\"id\":2},{\"id\":3}]", JSON_UTF_8)); client = LoggingClientBuilder.newClient(); } From 8d2bf35cd828ee76b97feaf190c0435ecf7e2886 Mon Sep 17 00:00:00 2001 From: Adrian Herscu Date: Thu, 3 Aug 2023 14:56:00 +0300 Subject: [PATCH 05/13] changed to bind server to a free port --- .../aherscu/qa/testing/example/AbstractMockServerTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qa-testing-example/src/main/java/dev/aherscu/qa/testing/example/AbstractMockServerTest.java b/qa-testing-example/src/main/java/dev/aherscu/qa/testing/example/AbstractMockServerTest.java index d1a5a5752c..41efb39660 100644 --- a/qa-testing-example/src/main/java/dev/aherscu/qa/testing/example/AbstractMockServerTest.java +++ b/qa-testing-example/src/main/java/dev/aherscu/qa/testing/example/AbstractMockServerTest.java @@ -56,8 +56,8 @@ abstract public class AbstractMockServerTest Date: Fri, 4 Aug 2023 21:12:54 +0300 Subject: [PATCH 06/13] removed unnecessary method --- .../java/dev/aherscu/qa/jgiven/commons/utils/StageEx.java | 8 -------- 1 file changed, 8 deletions(-) diff --git a/qa-jgiven-commons/src/main/java/dev/aherscu/qa/jgiven/commons/utils/StageEx.java b/qa-jgiven-commons/src/main/java/dev/aherscu/qa/jgiven/commons/utils/StageEx.java index a0b8e40237..13e9235733 100644 --- a/qa-jgiven-commons/src/main/java/dev/aherscu/qa/jgiven/commons/utils/StageEx.java +++ b/qa-jgiven-commons/src/main/java/dev/aherscu/qa/jgiven/commons/utils/StageEx.java @@ -99,14 +99,6 @@ public class StageEx> extends Stage { @ExpectedScenarioState public CurrentStep currentStep; - @Override - @SneakyThrows - @Deprecated // the description no longer appears in report - public final SELF $(final String description, - @Hidden final StepFunction function) { - return super.$(description, function); - } - protected final SELF alert( final Consumer action, final WebDriver driver) { From bfa77c932b44d33ec3d577bb3bb82d98307ff8ad Mon Sep 17 00:00:00 2001 From: Adrian Herscu Date: Fri, 4 Aug 2023 21:13:34 +0300 Subject: [PATCH 07/13] added error with connection dropping --- .../tutorial6/GenericMockServerRestTest.java | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/qa-testing-example/src/test/java/dev/aherscu/qa/testing/example/scenarios/tutorial6/GenericMockServerRestTest.java b/qa-testing-example/src/test/java/dev/aherscu/qa/testing/example/scenarios/tutorial6/GenericMockServerRestTest.java index d66683ecd2..3493820d93 100644 --- a/qa-testing-example/src/test/java/dev/aherscu/qa/testing/example/scenarios/tutorial6/GenericMockServerRestTest.java +++ b/qa-testing-example/src/test/java/dev/aherscu/qa/testing/example/scenarios/tutorial6/GenericMockServerRestTest.java @@ -17,6 +17,7 @@ import static java.util.Arrays.*; import static org.hamcrest.Matchers.*; +import static org.mockserver.model.HttpError.*; import static org.mockserver.model.HttpRequest.*; import static org.mockserver.model.HttpResponse.*; import static org.mockserver.model.MediaType.*; @@ -71,6 +72,20 @@ public void shouldRetrieveJsonFieldFromFakeRestService() { .the_response_contents(asList( new JsonAssertion<>("$[0].id", greaterThan(0)), new JsonAssertion<>("$[1].id", equalTo(2)))); + + when() + .connecting_to(mockServerUri()) + .and().appending_path("drop-connection") + .and().$("trying to access", + self -> self.safely(__ -> self.getting_the_response())); + + then() + .$("the requests were sent to mock server", + __ -> mockServer.verify( + request() + .withPath("/some-id"), + request() + .withPath("/drop-connection"))); } @AfterClass @@ -86,6 +101,11 @@ private void beforeClassOpenRestClient() { .respond(response() .withBody("[{\"id\":1},{\"id\":2},{\"id\":3}]", JSON_UTF_8)); + mockServer + .when(request() // GET is implied + .withPath("/drop-connection")) + .error(error().withDropConnection(true)); + client = LoggingClientBuilder.newClient(); } } From d04540a90b7f7edbc2bbf127eabe4555f7d656de Mon Sep 17 00:00:00 2001 From: Adrian Herscu Date: Sat, 5 Aug 2023 18:12:53 +0300 Subject: [PATCH 08/13] changed to verify separately not as a sequence --- .../tutorial6/GenericMockServerRestTest.java | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/qa-testing-example/src/test/java/dev/aherscu/qa/testing/example/scenarios/tutorial6/GenericMockServerRestTest.java b/qa-testing-example/src/test/java/dev/aherscu/qa/testing/example/scenarios/tutorial6/GenericMockServerRestTest.java index 3493820d93..7d663d4d8e 100644 --- a/qa-testing-example/src/test/java/dev/aherscu/qa/testing/example/scenarios/tutorial6/GenericMockServerRestTest.java +++ b/qa-testing-example/src/test/java/dev/aherscu/qa/testing/example/scenarios/tutorial6/GenericMockServerRestTest.java @@ -71,7 +71,10 @@ public void shouldRetrieveJsonFieldFromFakeRestService() { then() .the_response_contents(asList( new JsonAssertion<>("$[0].id", greaterThan(0)), - new JsonAssertion<>("$[1].id", equalTo(2)))); + new JsonAssertion<>("$[1].id", equalTo(2)))) + .and().$("the requests were sent to mock server", + __ -> mockServer.verify(request() + .withPath("/some-id"))); when() .connecting_to(mockServerUri()) @@ -81,11 +84,8 @@ public void shouldRetrieveJsonFieldFromFakeRestService() { then() .$("the requests were sent to mock server", - __ -> mockServer.verify( - request() - .withPath("/some-id"), - request() - .withPath("/drop-connection"))); + __ -> mockServer.verify(request() + .withPath("/drop-connection"))); } @AfterClass @@ -95,17 +95,17 @@ private void afterClassCloseRestClient() { @BeforeClass private void beforeClassOpenRestClient() { + mockServer + .when(request() // GET is implied + .withPath("/drop-connection")) + .error(error().withDropConnection(true)); + mockServer .when(request() // GET is implied .withPath("/some-id")) .respond(response() .withBody("[{\"id\":1},{\"id\":2},{\"id\":3}]", JSON_UTF_8)); - mockServer - .when(request() // GET is implied - .withPath("/drop-connection")) - .error(error().withDropConnection(true)); - client = LoggingClientBuilder.newClient(); } } From c5608781c1cf8e6948328552ae95aa0e02593ebf Mon Sep 17 00:00:00 2001 From: Adrian Herscu Date: Sat, 5 Aug 2023 18:22:45 +0300 Subject: [PATCH 09/13] separated rest client initialization from mock initialization --- .../tutorial6/GenericMockServerRestTest.java | 11 +++++++---- .../example/scenarios/tutorial6/GenericRestTest.java | 10 +++++++--- .../example/scenarios/tutorial6/StatefulRestTest.java | 5 ++++- 3 files changed, 18 insertions(+), 8 deletions(-) diff --git a/qa-testing-example/src/test/java/dev/aherscu/qa/testing/example/scenarios/tutorial6/GenericMockServerRestTest.java b/qa-testing-example/src/test/java/dev/aherscu/qa/testing/example/scenarios/tutorial6/GenericMockServerRestTest.java index 7d663d4d8e..dfa6b4cfc2 100644 --- a/qa-testing-example/src/test/java/dev/aherscu/qa/testing/example/scenarios/tutorial6/GenericMockServerRestTest.java +++ b/qa-testing-example/src/test/java/dev/aherscu/qa/testing/example/scenarios/tutorial6/GenericMockServerRestTest.java @@ -94,18 +94,21 @@ private void afterClassCloseRestClient() { } @BeforeClass - private void beforeClassOpenRestClient() { + private void beforeClassAddExpectations() { mockServer - .when(request() // GET is implied - .withPath("/drop-connection")) - .error(error().withDropConnection(true)); + .when(request() // GET is implied + .withPath("/drop-connection")) + .error(error().withDropConnection(true)); mockServer .when(request() // GET is implied .withPath("/some-id")) .respond(response() .withBody("[{\"id\":1},{\"id\":2},{\"id\":3}]", JSON_UTF_8)); + } + @BeforeClass + private void beforeClassOpenRestClient() { client = LoggingClientBuilder.newClient(); } } diff --git a/qa-testing-example/src/test/java/dev/aherscu/qa/testing/example/scenarios/tutorial6/GenericRestTest.java b/qa-testing-example/src/test/java/dev/aherscu/qa/testing/example/scenarios/tutorial6/GenericRestTest.java index 0b9fd1798c..42eb932e62 100644 --- a/qa-testing-example/src/test/java/dev/aherscu/qa/testing/example/scenarios/tutorial6/GenericRestTest.java +++ b/qa-testing-example/src/test/java/dev/aherscu/qa/testing/example/scenarios/tutorial6/GenericRestTest.java @@ -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.*; @@ -46,7 +46,8 @@ justification = "JGiven framework limitation") @SuppressWarnings({ "boxing" }) public final class GenericRestTest - extends AbstractWireMockTest, RestActions, RestVerifications> { + extends + AbstractWireMockTest, RestActions, RestVerifications> { private Client client; @@ -76,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(); } } diff --git a/qa-testing-example/src/test/java/dev/aherscu/qa/testing/example/scenarios/tutorial6/StatefulRestTest.java b/qa-testing-example/src/test/java/dev/aherscu/qa/testing/example/scenarios/tutorial6/StatefulRestTest.java index 8cb4ecd890..0fba42566b 100644 --- a/qa-testing-example/src/test/java/dev/aherscu/qa/testing/example/scenarios/tutorial6/StatefulRestTest.java +++ b/qa-testing-example/src/test/java/dev/aherscu/qa/testing/example/scenarios/tutorial6/StatefulRestTest.java @@ -107,7 +107,7 @@ private void afterClassCloseRestClient() { } @BeforeClass - private void beforeClassOpenRestClient() { + private void beforeClassAddStubs() { val AUTHENTICATED = "AUTHENTICATED"; wireMockServer.stubFor(post(urlEqualTo("/authenticate")) .inScenario(StatefulRestTest.class.getName()) @@ -125,7 +125,10 @@ private void beforeClassOpenRestClient() { .whenScenarioStateIs(STARTED) .willReturn(unauthorized()) .willSetStateTo(STARTED)); + } + @BeforeClass + private void beforeClassOpenRestClient() { client = LoggingClientBuilder.newClient(); } } From 9e54bfc0aab80b3762ae00448a1640ce3318cd7a Mon Sep 17 00:00:00 2001 From: Adrian Herscu Date: Sat, 5 Aug 2023 18:48:09 +0300 Subject: [PATCH 10/13] separated tests and added verification by expectation id --- .../tutorial6/GenericMockServerRestTest.java | 67 ++++++++++++++----- 1 file changed, 52 insertions(+), 15 deletions(-) diff --git a/qa-testing-example/src/test/java/dev/aherscu/qa/testing/example/scenarios/tutorial6/GenericMockServerRestTest.java b/qa-testing-example/src/test/java/dev/aherscu/qa/testing/example/scenarios/tutorial6/GenericMockServerRestTest.java index dfa6b4cfc2..a9e824b209 100644 --- a/qa-testing-example/src/test/java/dev/aherscu/qa/testing/example/scenarios/tutorial6/GenericMockServerRestTest.java +++ b/qa-testing-example/src/test/java/dev/aherscu/qa/testing/example/scenarios/tutorial6/GenericMockServerRestTest.java @@ -15,6 +15,7 @@ */ package dev.aherscu.qa.testing.example.scenarios.tutorial6; +import static dev.aherscu.qa.testing.utils.StringUtilsExtensions.*; import static java.util.Arrays.*; import static org.hamcrest.Matchers.*; import static org.mockserver.model.HttpError.*; @@ -22,8 +23,12 @@ 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.testng.annotations.*; import dev.aherscu.qa.jgiven.commons.tags.*; @@ -51,15 +56,38 @@ public final class GenericMockServerRestTest extends AbstractMockServerTest, RestActions, RestVerifications> { - private Client client; + 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))); + } - /** - * Should retrieve a JSON field from a REST service - */ @Test @Reference("159") @SneakyThrows - public void shouldRetrieveJsonFieldFromFakeRestService() { + public void shouldVerifyAccessByDefinition() { given() .a_REST_client(client); @@ -72,9 +100,16 @@ public void shouldRetrieveJsonFieldFromFakeRestService() { .the_response_contents(asList( new JsonAssertion<>("$[0].id", greaterThan(0)), new JsonAssertion<>("$[1].id", equalTo(2)))) - .and().$("the requests were sent to mock server", - __ -> mockServer.verify(request() - .withPath("/some-id"))); + .and().$("the some id request was sent", + __ -> mockServer.verify(request().withPath("/some-id"))); + } + + @Test + @Reference("159") + @SneakyThrows + public void shouldVeryAccessOfDroppedConnection() { + given() + .a_REST_client(client); when() .connecting_to(mockServerUri()) @@ -83,7 +118,7 @@ public void shouldRetrieveJsonFieldFromFakeRestService() { self -> self.safely(__ -> self.getting_the_response())); then() - .$("the requests were sent to mock server", + .$("the drop connection request was sent", __ -> mockServer.verify(request() .withPath("/drop-connection"))); } @@ -95,14 +130,16 @@ private void afterClassCloseRestClient() { @BeforeClass private void beforeClassAddExpectations() { - mockServer - .when(request() // GET is implied - .withPath("/drop-connection")) + expectations = mockServer // GET is implied + .when(request().withPath("/dummy")) + .respond(response(EMPTY)); + + mockServer // GET is implied + .when(request().withPath("/drop-connection")) .error(error().withDropConnection(true)); - mockServer - .when(request() // GET is implied - .withPath("/some-id")) + mockServer // GET is implied + .when(request().withPath("/some-id")) .respond(response() .withBody("[{\"id\":1},{\"id\":2},{\"id\":3}]", JSON_UTF_8)); } From e742b25e8323630af6300b36ccdc972ba15c5874 Mon Sep 17 00:00:00 2001 From: Adrian Herscu Date: Mon, 7 Aug 2023 09:33:18 +0300 Subject: [PATCH 11/13] fixed wording --- .../example/scenarios/tutorial6/GenericMockServerRestTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qa-testing-example/src/test/java/dev/aherscu/qa/testing/example/scenarios/tutorial6/GenericMockServerRestTest.java b/qa-testing-example/src/test/java/dev/aherscu/qa/testing/example/scenarios/tutorial6/GenericMockServerRestTest.java index a9e824b209..96ad2baf7a 100644 --- a/qa-testing-example/src/test/java/dev/aherscu/qa/testing/example/scenarios/tutorial6/GenericMockServerRestTest.java +++ b/qa-testing-example/src/test/java/dev/aherscu/qa/testing/example/scenarios/tutorial6/GenericMockServerRestTest.java @@ -107,7 +107,7 @@ public void shouldVerifyAccessByDefinition() { @Test @Reference("159") @SneakyThrows - public void shouldVeryAccessOfDroppedConnection() { + public void shouldVerifyAccessOfDroppedConnection() { given() .a_REST_client(client); From 8929efa7d7e2d4531e8df73b34b73c12f85c5a0a Mon Sep 17 00:00:00 2001 From: Adrian Herscu Date: Mon, 7 Aug 2023 09:34:33 +0300 Subject: [PATCH 12/13] testing with exactly -- expectation is removed correctly --- .../tutorial6/GenericMockServerRestTest.java | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/qa-testing-example/src/test/java/dev/aherscu/qa/testing/example/scenarios/tutorial6/GenericMockServerRestTest.java b/qa-testing-example/src/test/java/dev/aherscu/qa/testing/example/scenarios/tutorial6/GenericMockServerRestTest.java index 96ad2baf7a..841e1308fe 100644 --- a/qa-testing-example/src/test/java/dev/aherscu/qa/testing/example/scenarios/tutorial6/GenericMockServerRestTest.java +++ b/qa-testing-example/src/test/java/dev/aherscu/qa/testing/example/scenarios/tutorial6/GenericMockServerRestTest.java @@ -17,7 +17,9 @@ 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.*; @@ -29,6 +31,7 @@ import org.mockserver.mock.*; import org.mockserver.model.*; +import org.mockserver.verify.*; import org.testng.annotations.*; import dev.aherscu.qa.jgiven.commons.tags.*; @@ -104,6 +107,27 @@ public void shouldVerifyAccessByDefinition() { __ -> 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 @@ -134,6 +158,10 @@ private void beforeClassAddExpectations() { .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)); From aecd3f39b4a9c355b5804d6e8bc045139ea374d5 Mon Sep 17 00:00:00 2001 From: Adrian Herscu Date: Mon, 7 Aug 2023 09:37:50 +0300 Subject: [PATCH 13/13] improved to support out-of-process mockserver --- .../example/AbstractMockServerTest.java | 41 ++++++++++++++++--- 1 file changed, 36 insertions(+), 5 deletions(-) diff --git a/qa-testing-example/src/main/java/dev/aherscu/qa/testing/example/AbstractMockServerTest.java b/qa-testing-example/src/main/java/dev/aherscu/qa/testing/example/AbstractMockServerTest.java index 41efb39660..e1d21b96ff 100644 --- a/qa-testing-example/src/main/java/dev/aherscu/qa/testing/example/AbstractMockServerTest.java +++ b/qa-testing-example/src/main/java/dev/aherscu/qa/testing/example/AbstractMockServerTest.java @@ -15,10 +15,12 @@ */ 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.*; @@ -26,6 +28,8 @@ 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 @@ -45,24 +49,51 @@ @edu.umd.cs.findbugs.annotations.SuppressFBWarnings( value = "BC_UNCONFIRMED_CAST_OF_RETURN_VALUE", justification = "JGiven framework limitation") +@Slf4j abstract public class AbstractMockServerTest & ScenarioType, WHEN extends GenericActions & ScenarioType, THEN extends GenericVerifications & ScenarioType> extends UnitilsScenarioTest { - protected final ClientAndServer mockServer; + public static final int DEFAULT_PORT = 1080; + + protected final MockServerClient mockServer; + + private final boolean usingOutOfProcessMockServer; /** - * Initializes with {@link BaseConfiguration}. + * If port 1080 is free will initiate an in-process MockServer, otherwise + * will try connecting to port 1080. */ protected AbstractMockServerTest() { super(BaseConfiguration.class); - // NOTE port 0 means any free port - mockServer = ClientAndServer.startClientAndServer(0); + + 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() { - mockServer.stop(); + if (!usingOutOfProcessMockServer) + mockServer.stop(); } protected URI mockServerUri() {