From fa485438a29de4e454800b2e4ccf0919df38b6fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Martins?= Date: Sat, 18 Apr 2020 17:57:05 +0100 Subject: [PATCH] revamp current system properties management (#13) --- README.md | 79 ++++---- checkstyle.xml | 30 ++- pom.xml | 8 +- src/test/java/base/Base.java | 7 +- src/test/java/base/Browser.java | 13 +- src/test/java/base/BrowserType.java | 16 -- src/test/java/base/DriverContext.java | 50 ++--- src/test/java/base/FrameworkBootstrap.java | 55 ++---- src/test/java/base/MockContext.java | 29 +-- src/test/java/pageobjects/PageObject.java | 18 +- .../pageobjects/components/BaseComponent.java | 6 +- src/test/java/pageobjects/pages/BasePage.java | 2 +- .../java/pageobjects/pages/ExamplePage.java | 5 +- .../selectors/ExampleSelector.java | 5 +- src/test/java/tests/BaseTest.java | 5 +- src/test/java/tests/ExampleTest.java | 10 +- src/test/java/utils/config/Config.java | 174 ------------------ src/test/java/utils/config/ConfigReader.java | 86 --------- .../utils/config/CustomConfiguration.java | 25 +++ .../config/CustomConfigurationHolder.java | 13 ++ src/test/java/utils/constants/Constants.java | 3 + .../utils/listeners/ExtentReportListener.java | 1 - ...kListener.java => MockServerListener.java} | 13 +- src/test/java/utils/mocks/Mock.java | 10 +- src/test/java/utils/mocks/MockDefinition.java | 17 +- src/test/java/utils/mocks/MockParser.java | 2 +- .../java/utils/mocks/model/MockCookie.java | 6 - .../java/utils/mocks/model/MockHeader.java | 8 +- .../mocks/model/MockQueryStringParameter.java | 8 +- .../java/utils/mocks/model/MockRequest.java | 41 ++--- .../java/utils/mocks/model/MockResponse.java | 21 ++- .../java/utils/reports/ExtentManager.java | 8 +- .../resources/config/config.local.properties | 6 - .../resources/config/config.qa.properties | 7 - src/test/resources/tests.properties | 1 - testng.xml | 4 - 36 files changed, 243 insertions(+), 549 deletions(-) delete mode 100644 src/test/java/base/BrowserType.java delete mode 100644 src/test/java/utils/config/Config.java delete mode 100644 src/test/java/utils/config/ConfigReader.java create mode 100644 src/test/java/utils/config/CustomConfiguration.java create mode 100644 src/test/java/utils/config/CustomConfigurationHolder.java rename src/test/java/utils/listeners/{MockListener.java => MockServerListener.java} (85%) delete mode 100644 src/test/resources/config/config.local.properties delete mode 100644 src/test/resources/config/config.qa.properties delete mode 100644 src/test/resources/tests.properties diff --git a/README.md b/README.md index 1876d8b..e4bac9c 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # ui-automation-bootstrap -A foundation for selenium based ui automation projects using _[selenide](https://github.com/selenide/selenide)_ ✨ +A template for selenium based ui automation projects using _[selenide](https://github.com/selenide/selenide)_ ✨ [![badge-jdk](https://img.shields.io/badge/jdk-8-green.svg)](http://www.oracle.com/technetwork/java/javase/downloads/index.html) ![Languages](https://img.shields.io/github/languages/top/sergiomartins8/ui-automation-bootstrap) @@ -34,16 +34,16 @@ public void testExample() { ## About -The goal is to build a solid and generic foundation so that Test Automation Engineers as myself are able to bootstrap new or on going ui Selenium based automation projects with ease. +The goal is to build a solid and generic template so that Test Automation Engineers as myself are able to bootstrap new or ongoing ui Selenium based automation projects with ease. ##### Using the goods of * _[Selenide](https://github.com/selenide/selenide)_ - A selenium wrapper for concise UI tests * _[WebDriverManager](https://github.com/bonigarcia/webdrivermanager)_ - Downloads the required driver during runtime. May be configured on `DriverContext` under `/base` -* _[MockServer](https://www.mock-server.com/) 🐳_ - Enables the ability to mock _http_ requests and responses (check [mocking](#mocking-requests-and-responses) section) -* _[ExtentReports](https://extentreports.com/)_ - Provides full test reports. Takes screenshots upon test failure by default (check [reports](#reports) section) -* _[SonarQube](https://www.sonarqube.org/) 🐳_ - A static analysis tool. Executable through `$ mvn sonar:sonar -Dsonar.host.url=http://<>:9090` +* _[MockServer](https://www.mock-server.com/) 🐳_ - Enables the ability to mock _http_ requests and responses (check [MockServerListener](#mockserverlistener) section) +* _[ExtentReports](https://extentreports.com/)_ - Provides full test reports. Takes screenshots upon test failure by default (check [ExtentReportListener](#extentreportlistener) section) +* _[SonarQube](https://www.sonarqube.org/) 🐳_ - A static analysis tool. Executable through: `$ mvn sonar:sonar -Dsonar.host.url=http://<>:9090` * _[SeleniumGrid](https://github.com/SeleniumHQ/docker-selenium) 🐳_ - Allows to scale the test executing as well as providing the required browser types -* _[Checkstyle](https://maven.apache.org/plugins/maven-checkstyle-plugin/)_ - Code linter. Executable through `$ mvn validate` +* _[Checkstyle](https://maven.apache.org/plugins/maven-checkstyle-plugin/)_ - Code linter. Executable through: `$ mvn validate` > _🐳 stands for dockerized_ @@ -53,62 +53,57 @@ The goal is to build a solid and generic foundation so that Test Automation Engi $ git clone https://github.com/sergiomartins8/ui-automation-bootstrap.git $ cd ui-automation-bootstrap/ $ docker-compose up -d -$ mvn test -Dtestnames=Example +$ mvn test [-Dselenide.remote=http://localhost:4444/wd/hub] ``` -### Runtime properties +## Documentation -Configurable on `pom.xml` +#### System properties ````shell script -$ mvn test -Dtestnames=Example [-Dbrowser=chrome|firefox] [-Dparallel=methods|classes|tests] [-DthreadCount=n] [-Dlistener="utils.listeners.ExampleListener"] [-Denvironment="$env"] +$ mvn test [-Dmock.server.url= -Dmock.server.port=] \ + [-Dparallel=] \ + [-DthreadCount=] \ + [-Dlistener=] ```` -> **testnames** (mandatory) - Tests to be executed (check `testng.xml`) -> -> **browser** (optional, default `chrome`) - Browser to execute tests -> -> **parallel** (optional, default `false`) - Tells testng the method of parallel test execution -> -> **threadCount** (optional, default `1`) - How many threads to use during test execution -> -> **listener** (optional, default `MockListener` and `ExtentReportListener`) - Additional listener to be used during test execution (check `testng.xml`) -> -> **environment** (optional, default `qa`) - Environment configuration to be loaded. However, it's overwritten when `run.tests.local=true`, falling back to default `local`. +| Property | Description | Default | +| --------------------------------- | --------------------------------- | ----------------------------------------- | +| `mock.server.address` | Mock server address | `null` _(Mock server it not used)_ | +| `parallel` | Enables parallel threads | `false` | +| `threadCount` | The default number of threads to use when running tests in parallel | `1` | +| `listener` | A comma-separated list of java classes that can be found on your classpath | `null` _(Listeners not being used)_ | -## Documentation +#### More system properties -### Configuration overview +Using the goods of selenide, you can also inject its system properties. -Default configurations available under `resources/`: +##### Example: +```shell script +mvn test -Dselenide.remote=http://localhost:4444/wd/hub \ + -Dselenide.headless=true \ + -Dselenide.browser=firefox \ + -Dselenide.baseUrl=http:/google.com +``` -|variable|path|default|description| -|----|----|----|----| -|`run.tests.local`|`tests.properties`|`true`|Running tests locally to avoid using the `RemoteWebDriver` (May be configured)| -|`browser.type`|`config/${environment}`|`chrome`|Chooses in which browser tests rare executed| -|`base.url`|`config/${environment}`|`http://google.com`|Base url; e.g. `open("")` with an empty string opens the browser on the base url| -|`screenshots`|`config/${environment}`|`false`|Selenide screenshots on test failure (However, screenshots are taken by the `ExtentReportListener`) by default| -|`headless`|`config/${environment}`|`false` on local config otherwise `true`|Test execution in headless mode| -|`remote.webdriver.url`|`config/${environment}`|`http://localhost:4444/wd/hub` (http://<>:4444/wd/hub)|Selenium hub deployed through `docker-compose.yaml`| -|`mock.server.url`|`config/${environment}`|`localhost` (<>)|Mock server ip deployed through `docker-compose.yaml`| -|`mock.server.port`|`config/${environment}`|`3000`|Mock server port exposed to the outside (check `docker-compose.yaml`)| +More about selenide's configuration settings and documentation [here](https://selenide.org/javadoc/current/com/codeborne/selenide/Configuration.html). -> If you add new configurations make sure you edit the `Config` builder and `ConfigReader` accordingly, under `utils/config/`. +### Listeners -### Mocking requests and responses +There are a couple listeners available _(however, disabled by default)_. +The `MockServerListener` and `ExtentReportListener`, located under `utils/listeners`. -Using _[MockServer](https://www.mock-server.com/)_ it's possible to inject mocks during runtime using the `@Mock` annotation, which is extracted by the `MockListener` (under `utils/listeners/`): +#### MockServerListener + +Using _[MockServer](https://www.mock-server.com/)_, it allows injecting mocks during runtime listening to `@Mock` annotations. ````java @Mock(path = {"path1", "path2", ...}) ```` -> Make sure you edit `MockListener` accordingly to match your needs. - -### Reports - -Reports are automatically generated using _[ExtentReports](https://extentreports.com/)_ when using the `ExtentReportListener` under `/reports`. +#### ExtentReportListener +Using _[ExtentReports](https://extentreports.com/)_, it automatically generates reports after test execution. Stored under `reports/ExtentReport.html`. In addition, and by default, screenshots are taken upon test failure and attached to the report. ![](docs/reports.gif) diff --git a/checkstyle.xml b/checkstyle.xml index bfed362..6dcfbbf 100644 --- a/checkstyle.xml +++ b/checkstyle.xml @@ -17,11 +17,6 @@ - - - - - @@ -57,8 +52,31 @@ - + + + + + + + + + + + + + + + + + + + + + + + diff --git a/pom.xml b/pom.xml index 80ce83b..d9809ad 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.github.sergiomartins8 ui-automation-bootstrap - 1.3.0 + 1.4.0 1.8 @@ -42,13 +42,9 @@ listener ${listener} - - testnames - ${testnames} - - testng.xml testng.xml path <--> + testng.xml diff --git a/src/test/java/base/Base.java b/src/test/java/base/Base.java index 2dac723..a2c4d9b 100644 --- a/src/test/java/base/Base.java +++ b/src/test/java/base/Base.java @@ -5,18 +5,15 @@ /** * Bridge between the framework level and the tests. - *

+ *
* Class' single responsibility is to setup and teardown the webdriver. - *

*/ public abstract class Base extends FrameworkBootstrap { @BeforeMethod public void setupWebDriver() { logger().info("Initialize webdriver"); - DriverContext.initializeRemoteWebDriver(getConfig().getBrowserType(), - getConfig().runTestsLocal(), - getConfig().getRemoteWebDriverUrl()); + DriverContext.initializeRemoteWebDriver(); } @AfterMethod diff --git a/src/test/java/base/Browser.java b/src/test/java/base/Browser.java index 8c2a482..b70387b 100644 --- a/src/test/java/base/Browser.java +++ b/src/test/java/base/Browser.java @@ -4,17 +4,14 @@ /** * Holds actions that a user is able to perform on a browser. - *

+ *
* Browser actions are performed through {@link DriverContext} - * e.g. {@link DriverContext#getBrowser().action()} - *

+ *
+ * Example: {@code DriverContext.browser.maximize()} */ -public final class Browser { +public class Browser { - /** - * Webdriver instance which the browser is using. - */ - private WebDriver driver; + private final WebDriver driver; public Browser(WebDriver driver) { this.driver = driver; diff --git a/src/test/java/base/BrowserType.java b/src/test/java/base/BrowserType.java deleted file mode 100644 index e43f7bd..0000000 --- a/src/test/java/base/BrowserType.java +++ /dev/null @@ -1,16 +0,0 @@ -package base; - -/** - * Browser types that the test execution supports. - */ -public enum BrowserType { - /** - * Chrome browser. - */ - chrome, - - /** - * Firefox browser. - */ - firefox -} diff --git a/src/test/java/base/DriverContext.java b/src/test/java/base/DriverContext.java index 8d111ae..c6e2983 100644 --- a/src/test/java/base/DriverContext.java +++ b/src/test/java/base/DriverContext.java @@ -1,5 +1,6 @@ package base; +import com.codeborne.selenide.Configuration; import com.codeborne.selenide.WebDriverRunner; import io.github.bonigarcia.wdm.WebDriverManager; import org.openqa.selenium.Capabilities; @@ -17,32 +18,35 @@ /** * Holds methods that initialize (and teardown) the required driver; locally or remotely. - * Also, an instance to the {@link Browser} in which browser actions can be performed. - *

- * Browser actions are performed through {@link DriverContext} - * e.g. {@link DriverContext#getBrowser().action()} - *

+ *
+ * Also, an instance to the {@link Browser} in which browser actions can be performed */ public final class DriverContext { /** - * Browser instance which is going to receive the webdriver. + * Browser instance. */ - private static Browser browser; + public static Browser browser; + /** + * Private constructor to avoid instantiation. + */ private DriverContext() { } - public static void initializeRemoteWebDriver(BrowserType browserType, - boolean runTestsLocal, - String remoteWebDriverUrl) { + /** + * Initializes the webdriver accordingly to the requested browser. + * + * @throws UnsupportedOperationException in case of the required browser is not covered within the current switch case. + */ + public static void initializeRemoteWebDriver() { RemoteWebDriver driver; - switch (browserType) { - case chrome: - driver = setupChromeDriver(runTestsLocal, remoteWebDriverUrl); + switch (Configuration.browser) { + case "chrome": + driver = setupChromeDriver(); break; - case firefox: - driver = setupFirefoxDriver(runTestsLocal, remoteWebDriverUrl); + case "firefox": + driver = setupFirefoxDriver(); break; default: throw new UnsupportedOperationException("The required browser is not supported"); @@ -60,19 +64,15 @@ public static WebDriver getRemoteWebDriver() { return getWebDriver(); } - public static Browser getBrowser() { - return browser; - } - - private static RemoteWebDriver setupChromeDriver(boolean runTestsLocal, String remoteWebDriverUrl) { + private static RemoteWebDriver setupChromeDriver() { WebDriverManager.chromedriver().setup(); RemoteWebDriver driver = null; - if (runTestsLocal) { + if (Configuration.remote == null) { driver = new ChromeDriver(); } else { try { Capabilities capabilities = new ChromeOptions(); - driver = new RemoteWebDriver(new URL(remoteWebDriverUrl), capabilities); + driver = new RemoteWebDriver(new URL(Configuration.remote), capabilities); } catch (MalformedURLException e) { e.printStackTrace(); } @@ -80,15 +80,15 @@ private static RemoteWebDriver setupChromeDriver(boolean runTestsLocal, String r return driver; } - private static RemoteWebDriver setupFirefoxDriver(boolean runTestsLocal, String remoteWebDriverUrl) { + private static RemoteWebDriver setupFirefoxDriver() { WebDriverManager.firefoxdriver().setup(); RemoteWebDriver driver = null; - if (runTestsLocal) { + if (Configuration.remote == null) { driver = new FirefoxDriver(); } else { try { Capabilities capabilities = new FirefoxOptions(); - driver = new RemoteWebDriver(new URL(remoteWebDriverUrl), capabilities); + driver = new RemoteWebDriver(new URL(Configuration.remote), capabilities); } catch (MalformedURLException e) { e.printStackTrace(); } diff --git a/src/test/java/base/FrameworkBootstrap.java b/src/test/java/base/FrameworkBootstrap.java index fe1062d..c63c52f 100644 --- a/src/test/java/base/FrameworkBootstrap.java +++ b/src/test/java/base/FrameworkBootstrap.java @@ -1,66 +1,35 @@ package base; -import com.codeborne.selenide.Configuration; -import org.mockserver.client.MockServerClient; -import org.testng.annotations.AfterMethod; -import org.testng.annotations.BeforeMethod; +import org.testng.annotations.AfterSuite; import org.testng.annotations.BeforeSuite; -import utils.config.Config; -import utils.config.ConfigReader; +import utils.config.CustomConfiguration; import utils.logging.Loggable; /** * Where it all starts. - *

+ *
* The framework is initialized here with all the required configurations. * It also takes care of the {@link MockContext} initialization and teardown. - *

*/ public abstract class FrameworkBootstrap implements Loggable { /** - * Builder that holds all the configurations to be reused at the framework level. - * Has to be static so that it can be shared across parallel executions. + * Tells {@link MockContext} to initialize the mock server if it is present as a system property. */ - private static Config config; - @BeforeSuite - public void initializeFramework() { - config = ConfigReader.populateConfigs(); - initializeSelenide(); - } - - /** - * Use {@code @BeforeMethod} when running tests in {@code parallel=methods}. Edit accordingly. - * If sequential test execution use {@code @BeforeSuite} annotation. - */ - @BeforeMethod public void initializeMockServer() { - MockContext.setMockServerClient(new MockServerClient(config.getMockServerAddress(), config.getMockServerPort())); + if (CustomConfiguration.mockServerAddress != null) { + MockContext.initializeMockServerClient(); + } } /** - * Related to the {@link #initializeMockServer()} method. - *

- * Use {@code @AfterMethod} when running tests in {@code parallel=methods}. Edit accordingly. - * If sequential test execution use {@code @AfterSuite} annotation. - *

+ * Tells {@link MockContext} to reset the mock server data if it is present as a system property. */ - @AfterMethod + @AfterSuite public void teardownMockServer() { - MockContext.resetMockServerClient(); - } - - public static Config getConfig() { - return config; - } - - /** - * Assigns current configuration values to internal Selenide variables that configure the whole framework. - */ - private void initializeSelenide() { - Configuration.baseUrl = config.getBaseUrl(); - Configuration.screenshots = config.takeScreenshots(); - Configuration.headless = config.isHeadless(); + if (CustomConfiguration.mockServerAddress != null) { + MockContext.resetMockServerClient(); + } } } diff --git a/src/test/java/base/MockContext.java b/src/test/java/base/MockContext.java index 4366c34..635c59a 100644 --- a/src/test/java/base/MockContext.java +++ b/src/test/java/base/MockContext.java @@ -1,28 +1,31 @@ package base; import org.mockserver.client.MockServerClient; -import utils.listeners.MockListener; +import utils.config.CustomConfiguration; + +import java.net.URI; /** - * Single responsibility is to hold a mock server object so that it can be used by the {@link MockListener}. - * Initialization and teardown of the {@link MockContext} is responsibility of the {@link FrameworkBootstrap} class. + * Context for objects for mocking purposes. */ -public final class MockContext { +public class MockContext { + + private static MockServerClient mockServerClient; /** - * Thread safe implementation that holds the mock server client. + * Initializes the mock server client based on {@link CustomConfiguration} settings. */ - private static ThreadLocal mockServerClientThreadLocal = new ThreadLocal<>(); - - public static MockServerClient getMockServerClient() { - return mockServerClientThreadLocal.get(); + public static void initializeMockServerClient() { + MockContext.mockServerClient = new MockServerClient( + URI.create(CustomConfiguration.mockServerAddress).getHost(), + URI.create(CustomConfiguration.mockServerAddress).getPort()); } - public static void setMockServerClient(MockServerClient mockServerClient) { - mockServerClientThreadLocal.set(mockServerClient); + public static void resetMockServerClient() { + mockServerClient.reset(); } - public static void resetMockServerClient() { - mockServerClientThreadLocal.get().reset(); + public static MockServerClient getMockServerClient() { + return mockServerClient; } } diff --git a/src/test/java/pageobjects/PageObject.java b/src/test/java/pageobjects/PageObject.java index 2fc4145..43c9dc6 100644 --- a/src/test/java/pageobjects/PageObject.java +++ b/src/test/java/pageobjects/PageObject.java @@ -1,29 +1,31 @@ package pageobjects; +import base.DriverContext; import org.openqa.selenium.JavascriptExecutor; -import org.openqa.selenium.support.ui.FluentWait; import org.openqa.selenium.support.ui.WebDriverWait; -import base.DriverContext; import utils.constants.Constants; import utils.logging.Loggable; /** - * Abstraction that represents the definition of a page object - * Contains methods that are reusable by any page object. + * Abstraction that represents the definition of a page object. + *
+ * Contains methods that are reusable by any page object * - * @param represents the type of the concrete page object. + * @param represents the type of the concrete page object */ public abstract class PageObject> implements Loggable { - /** - * a {@link FluentWait} for waiting util methods to be shared across page objects. - */ private static WebDriverWait wait; public PageObject() { wait = new WebDriverWait(DriverContext.getRemoteWebDriver(), Constants.TIME_OUT_SECONDS); } + /** + * Waits the page to be fully loaded. + * + * @return concrete page object of type T, after waiting for the page to be loaded + */ public T waitPageLoaded() { wait.until(webDriver -> ((JavascriptExecutor) DriverContext.getRemoteWebDriver()) .executeScript("return document.readyState") diff --git a/src/test/java/pageobjects/components/BaseComponent.java b/src/test/java/pageobjects/components/BaseComponent.java index 298c610..ddad5ed 100644 --- a/src/test/java/pageobjects/components/BaseComponent.java +++ b/src/test/java/pageobjects/components/BaseComponent.java @@ -10,14 +10,14 @@ * Base abstraction for concrete types of components. * * @param represents the type of the concrete component. - * e.g. {@code public class ConcreteComponent extends BaseComponent} + * Example: {@code public class ConcreteComponent extends BaseComponent} */ public abstract class BaseComponent> extends PageObject implements Loggable { /** - * Unique element that describes a component. + * Unique element that describes this component. */ - private SelenideElement element; + private final SelenideElement element; public BaseComponent(String selector) { element = $(selector); diff --git a/src/test/java/pageobjects/pages/BasePage.java b/src/test/java/pageobjects/pages/BasePage.java index 437fbc3..4de8316 100644 --- a/src/test/java/pageobjects/pages/BasePage.java +++ b/src/test/java/pageobjects/pages/BasePage.java @@ -6,7 +6,7 @@ * Base abstraction for concrete types of pages. * * @param represents the type of the concrete page. - * e.g. {@code public class ConcretePage extends BasePage} + * Example: {@code public class ConcretePage extends BasePage} */ public class BasePage> extends PageObject { } diff --git a/src/test/java/pageobjects/pages/ExamplePage.java b/src/test/java/pageobjects/pages/ExamplePage.java index d628ad6..b724e9e 100644 --- a/src/test/java/pageobjects/pages/ExamplePage.java +++ b/src/test/java/pageobjects/pages/ExamplePage.java @@ -5,10 +5,7 @@ public class ExamplePage extends BasePage { - /** - * {@link ExampleComponent} component within the current page object. - */ - private ExampleComponent exampleComponent; + private final ExampleComponent exampleComponent; public ExamplePage() { exampleComponent = new ExampleComponent(ExampleSelector.EXAMPLE_SELECTOR); diff --git a/src/test/java/pageobjects/selectors/ExampleSelector.java b/src/test/java/pageobjects/selectors/ExampleSelector.java index a6e118f..f798868 100644 --- a/src/test/java/pageobjects/selectors/ExampleSelector.java +++ b/src/test/java/pageobjects/selectors/ExampleSelector.java @@ -1,7 +1,10 @@ package pageobjects.selectors; -public final class ExampleSelector { +public class ExampleSelector { + /** + * Private constructor to avoid instantiation. + */ private ExampleSelector() { } diff --git a/src/test/java/tests/BaseTest.java b/src/test/java/tests/BaseTest.java index 1843c4c..fa699ec 100644 --- a/src/test/java/tests/BaseTest.java +++ b/src/test/java/tests/BaseTest.java @@ -5,9 +5,8 @@ /** * Base abstraction for concrete types of tests. * Holds the implementation of methods to be reusable across tests. - *

- * e.g. login methods, cookies, username and password credentials - *

+ *
+ * Example: login methods, cookies, username and password credentials */ public abstract class BaseTest extends Base { } diff --git a/src/test/java/tests/ExampleTest.java b/src/test/java/tests/ExampleTest.java index 1fb91e0..ecc622e 100644 --- a/src/test/java/tests/ExampleTest.java +++ b/src/test/java/tests/ExampleTest.java @@ -2,22 +2,14 @@ import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; -import org.testng.annotations.Listeners; import org.testng.annotations.Test; import pageobjects.pages.ExamplePage; -import utils.listeners.ExtentReportListener; -import utils.listeners.MockListener; import utils.mocks.Mock; import static com.codeborne.selenide.Selenide.open; -@Listeners({ExtentReportListener.class, - MockListener.class}) public class ExampleTest extends BaseTest { - /** - * Instance for the {@link ExamplePage} page object. - */ private ExamplePage examplePage; @BeforeMethod @@ -33,7 +25,7 @@ public void teardown() { @Test(description = "Open up a google page and search for the word 'dogs'") public void testExampleOne() { - open(""); // empty string opens the base URL defined on + open("http://google.com"); examplePage.exampleComponent() .waitPageLoaded() diff --git a/src/test/java/utils/config/Config.java b/src/test/java/utils/config/Config.java deleted file mode 100644 index aa23634..0000000 --- a/src/test/java/utils/config/Config.java +++ /dev/null @@ -1,174 +0,0 @@ -package utils.config; - -import base.BrowserType; - -/** - * Builder Config. - */ -public final class Config { - - /** - * True if tests are executed locally. - */ - private final boolean runTestsLocal; - - /** - * Browser type in which the tests are executed. - */ - private final BrowserType browserType; - - /** - * Solution's base url to be used across the framework. - */ - private final String baseUrl; - - /** - * Allow Selenide to take screenshots on test failure. - */ - private final boolean screenshots; - - /** - * Tells if the tests are going to be executed in headless mode. - */ - private final boolean headless; - - /** - * Holds the remote web driver URL for the selenium grid. - */ - private final String remoteWebDriverUrl; - - /** - * Mock server url. - */ - private final String mockServerAddress; - - /** - * Mock server port. - */ - private final int mockServerPort; - - private Config(Builder builder) { - this.runTestsLocal = builder.runTestsLocal; - this.browserType = builder.browserType; - this.baseUrl = builder.baseUrl; - this.screenshots = builder.screenshots; - this.headless = builder.headless; - this.remoteWebDriverUrl = builder.remoteWebDriverUrl; - this.mockServerAddress = builder.mockServerAddress; - this.mockServerPort = builder.mockServerPort; - } - - public boolean runTestsLocal() { - return runTestsLocal; - } - - public BrowserType getBrowserType() { - return browserType; - } - - public String getBaseUrl() { - return baseUrl; - } - - public boolean takeScreenshots() { - return screenshots; - } - - public boolean isHeadless() { - return headless; - } - - public String getRemoteWebDriverUrl() { - return remoteWebDriverUrl; - } - - public String getMockServerAddress() { - return mockServerAddress; - } - - public int getMockServerPort() { - return mockServerPort; - } - - public static class Builder { - - /** - * {@code runTestsLocal} builder. - */ - private final boolean runTestsLocal; - - /** - * {@code browserType} builder. - */ - private final BrowserType browserType; - - /** - * {@code baseUrl} builder. - */ - private String baseUrl; - - /** - * {@code screenshots} builder. - */ - private boolean screenshots; - - /** - * {@code headless} builder. - */ - private boolean headless; - - /** - * {@code remoteWebDriverUrl} builder. - */ - private String remoteWebDriverUrl; - - /** - * {@code mockServerAddress} builder. - */ - private String mockServerAddress; - - /** - * {@code mockServerPort} builder. - */ - private int mockServerPort; - - public Builder(boolean runTestsLocal, BrowserType browserType) { - this.runTestsLocal = runTestsLocal; - this.browserType = browserType; - } - - public Builder withBaseUrl(String baseUrl) { - this.baseUrl = baseUrl; - return this; - } - - public Builder withScreenshots(boolean screenshots) { - this.screenshots = screenshots; - return this; - } - - public Builder withHeadless(boolean headless) { - this.headless = headless; - return this; - } - - public Builder withRemoteWebDriverUrl(String remoteWebDriverUrl) { - this.remoteWebDriverUrl = remoteWebDriverUrl; - return this; - } - - public Builder withMockServerAddress(String mockServerAddress) { - this.mockServerAddress = mockServerAddress; - return this; - } - - public Builder withMockServerPort(int mockServerPort) { - this.mockServerPort = mockServerPort; - return this; - } - - public Config build() { - return new Config(this); - } - } -} diff --git a/src/test/java/utils/config/ConfigReader.java b/src/test/java/utils/config/ConfigReader.java deleted file mode 100644 index a4b9517..0000000 --- a/src/test/java/utils/config/ConfigReader.java +++ /dev/null @@ -1,86 +0,0 @@ -package utils.config; - -import base.BrowserType; - -import java.io.IOException; -import java.io.InputStream; -import java.util.Properties; - -/** - * Single responsibility of reading system properties as well as configurations from the resources - * to build the {@link Config} object that holds them. - */ -public final class ConfigReader { - - private ConfigReader() { - } - - public static Config populateConfigs() { - Properties properties = getPropertiesFromResource("tests.properties"); - - boolean runTestsLocal = System.getProperty("environment") != null && System.getProperty("environment").equals("local") - || Boolean.parseBoolean(properties.getProperty("run.tests.local")); - - if (runTestsLocal) { - return localConfiguration(); - } else { - return remoteConfiguration(); - } - } - - private static Config localConfiguration() { - Properties properties = getPropertiesFromResource("config/config.local.properties"); - - return new Config.Builder(true, getBrowserType(properties)) - .withBaseUrl(properties.getProperty("base.url")) - .withScreenshots(Boolean.parseBoolean(properties.getProperty("screenshots"))) - .withHeadless(Boolean.parseBoolean(properties.getProperty("headless"))) - .withMockServerAddress(properties.getProperty("mock.server.url")) - .withMockServerPort(Integer.parseInt(properties.getProperty("mock.server.port"))) - .build(); - } - - private static Config remoteConfiguration() { - String environment = System.getProperty("environment") != null - ? System.getProperty("environment") : "qa"; - - Properties properties = getPropertiesFromResource("config/config." + environment + ".properties"); - - return new Config.Builder(false, getBrowserType(properties)) - .withBaseUrl(properties.getProperty("base.url")) - .withScreenshots(Boolean.parseBoolean(properties.getProperty("screenshots"))) - .withHeadless(Boolean.parseBoolean(properties.getProperty("headless"))) - .withRemoteWebDriverUrl(properties.getProperty("remote.webdriver.url")) - .withMockServerAddress(properties.getProperty("mock.server.url")) - .withMockServerPort(Integer.parseInt(properties.getProperty("mock.server.port"))) - .build(); - } - - /** - * @param resource to be read into an input stream. - * @return properties from the {@code resource} parameter. - */ - private static Properties getPropertiesFromResource(String resource) { - InputStream inputStream = ConfigReader.class - .getClassLoader() - .getResourceAsStream(resource); - - Properties properties = new Properties(); - - try { - properties.load(inputStream); - } catch (IOException e) { - e.printStackTrace(); - } - return properties; - } - - /** - * @param properties environment configuration - * @return {@link BrowserType} based on system properties or (if null) the environment configuration - */ - private static BrowserType getBrowserType(Properties properties) { - return System.getProperty("browser") != null - ? BrowserType.valueOf(System.getProperty("browser")) : BrowserType.valueOf(properties.getProperty("browser.type")); - } -} diff --git a/src/test/java/utils/config/CustomConfiguration.java b/src/test/java/utils/config/CustomConfiguration.java new file mode 100644 index 0000000..5021648 --- /dev/null +++ b/src/test/java/utils/config/CustomConfiguration.java @@ -0,0 +1,25 @@ +package utils.config; + +/** + * Configuration settings. + *
+ * This class is designed so that every setting can be set either via system property or programmatically + *
+ * Please note that all fields are static, meaning that every change + * will immediately reflect in all threads (if you run tests in parallel) + */ +public class CustomConfiguration { + + /** + * Default configuration settings holder. + */ + private static final CustomConfigurationHolder DEFAULTS = new CustomConfigurationHolder(); + + /** + * URL for the mock server. + * Can be configured either programmatically or by system property: {@code -Dmock.server.address=localhost:3000} + *
+ * Default value: null (Mock server it not used) + */ + public static String mockServerAddress = DEFAULTS.mockServerAddress(); +} diff --git a/src/test/java/utils/config/CustomConfigurationHolder.java b/src/test/java/utils/config/CustomConfigurationHolder.java new file mode 100644 index 0000000..548128f --- /dev/null +++ b/src/test/java/utils/config/CustomConfigurationHolder.java @@ -0,0 +1,13 @@ +package utils.config; + +import utils.logging.Loggable; + +public class CustomConfigurationHolder implements Loggable { + + private final String mockServerAddress = System.getProperty("mock.server.address"); + + public String mockServerAddress() { + logger().debug("Mock server address: " + mockServerAddress); + return mockServerAddress; + } +} diff --git a/src/test/java/utils/constants/Constants.java b/src/test/java/utils/constants/Constants.java index 85e3d95..01c73a1 100644 --- a/src/test/java/utils/constants/Constants.java +++ b/src/test/java/utils/constants/Constants.java @@ -5,6 +5,9 @@ */ public final class Constants { + /** + * Private constructor to avoid instantiation. + */ private Constants() { } diff --git a/src/test/java/utils/listeners/ExtentReportListener.java b/src/test/java/utils/listeners/ExtentReportListener.java index befa428..0df7de0 100644 --- a/src/test/java/utils/listeners/ExtentReportListener.java +++ b/src/test/java/utils/listeners/ExtentReportListener.java @@ -50,7 +50,6 @@ public void onTestFailure(ITestResult iTestResult) { ExtentTestReport.getTest().fail(MarkupHelper.createLabel(iTestResult.getThrowable().getMessage(), ExtentColor.RED)); - // ExtentReports log and screenshot for failed tests try { ExtentTestReport.getTest().fail("Screenshot - ", MediaEntityBuilder.createScreenCaptureFromBase64String(getScreenshotBase64()).build()); diff --git a/src/test/java/utils/listeners/MockListener.java b/src/test/java/utils/listeners/MockServerListener.java similarity index 85% rename from src/test/java/utils/listeners/MockListener.java rename to src/test/java/utils/listeners/MockServerListener.java index 7f6fb74..4396539 100644 --- a/src/test/java/utils/listeners/MockListener.java +++ b/src/test/java/utils/listeners/MockServerListener.java @@ -15,19 +15,20 @@ import static org.mockserver.model.HttpResponse.response; /** - * {@link ITestListener} implementation responsible for mocking requests and responses during runtime - * based on the {@link Mock} annotation. - *

- * The mocks are injected recurring to the {@link MockContext}. - *

+ * {@link ITestListener} implementation responsible for mocking requests and responses + * during runtime, based on the {@link Mock} annotation. + *
+ * The {@link MockContext} object holds the mock server details. */ -public class MockListener implements ITestListener, Loggable { +public class MockServerListener implements ITestListener, Loggable { @Override public void onTestStart(ITestResult result) { extractMockAnnotation(result).ifPresent(mock -> { for (String path : mock.path()) { + logger().debug("Mocking file on path: " + path); MockDefinition mockDefinition = MockParser.toObject(path); + logger().debug("Mocking content: " + mockDefinition.prettyPrint()); MockContext.getMockServerClient() .when(request() .withMethod(requireNonNull(mockDefinition).getRequest().getMethod()) diff --git a/src/test/java/utils/mocks/Mock.java b/src/test/java/utils/mocks/Mock.java index 1d9d314..2a2f748 100644 --- a/src/test/java/utils/mocks/Mock.java +++ b/src/test/java/utils/mocks/Mock.java @@ -7,14 +7,16 @@ /** * Annotation to inject mocks during runtime. - *

* Mocks are required to be json files. - *

- * e.g. {@code @Mock({"path1", "path2", ...})} - *

+ *
+ * Example: {@code @Mock({"path1", "path2", ...})} */ @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface Mock { + + /** + * String array containing the json mock files' paths. + */ String[] path(); } diff --git a/src/test/java/utils/mocks/MockDefinition.java b/src/test/java/utils/mocks/MockDefinition.java index e22cf93..ce3aadc 100644 --- a/src/test/java/utils/mocks/MockDefinition.java +++ b/src/test/java/utils/mocks/MockDefinition.java @@ -1,16 +1,16 @@ package utils.mocks; +import com.google.gson.GsonBuilder; import utils.mocks.model.MockRequest; import utils.mocks.model.MockResponse; -/*** +/** * Concrete definition of a mock. - *

+ *
* A mock is represented by a {@link MockRequest} and by a {@link MockResponse} - *

*/ @SuppressWarnings("unused") -public final class MockDefinition { +public class MockDefinition { /** * Represents the mocked request. @@ -37,4 +37,13 @@ public void setResponse(MockResponse response) { public void setRequest(MockRequest request) { this.request = request; } + + /** + * Similar to a `toString()` implementation. Useful for debugging purposes. + * + * @return current object in a pretty json format. + */ + public String prettyPrint() { + return new GsonBuilder().setPrettyPrinting().create().toJson(this); + } } diff --git a/src/test/java/utils/mocks/MockParser.java b/src/test/java/utils/mocks/MockParser.java index a26490e..d8cb372 100644 --- a/src/test/java/utils/mocks/MockParser.java +++ b/src/test/java/utils/mocks/MockParser.java @@ -7,7 +7,7 @@ /** * Single responsibility of parsing json files into {@link MockDefinition} objects. */ -public final class MockParser { +public class MockParser { public static MockDefinition toObject(String jsonPath) { return new Gson().fromJson(new InputStreamReader(MockParser.class.getResourceAsStream(jsonPath)), MockDefinition.class); diff --git a/src/test/java/utils/mocks/model/MockCookie.java b/src/test/java/utils/mocks/model/MockCookie.java index f4f615d..3c383f4 100644 --- a/src/test/java/utils/mocks/model/MockCookie.java +++ b/src/test/java/utils/mocks/model/MockCookie.java @@ -6,14 +6,8 @@ @SuppressWarnings("unused") public class MockCookie { - /** - * Cookie name. - */ private String name; - /** - * Cookie value. - */ private String value; public String getName() { diff --git a/src/test/java/utils/mocks/model/MockHeader.java b/src/test/java/utils/mocks/model/MockHeader.java index 89aaaa0..49f999d 100644 --- a/src/test/java/utils/mocks/model/MockHeader.java +++ b/src/test/java/utils/mocks/model/MockHeader.java @@ -4,16 +4,10 @@ * Concrete definition of a mocked header. */ @SuppressWarnings("unused") -public final class MockHeader { +public class MockHeader { - /** - * Header name. - */ private String name; - /** - * Header values. - */ private String values; public String getName() { diff --git a/src/test/java/utils/mocks/model/MockQueryStringParameter.java b/src/test/java/utils/mocks/model/MockQueryStringParameter.java index 0232368..73e7639 100644 --- a/src/test/java/utils/mocks/model/MockQueryStringParameter.java +++ b/src/test/java/utils/mocks/model/MockQueryStringParameter.java @@ -6,16 +6,10 @@ * Concrete definition of a mocked query parameter. */ @SuppressWarnings("unused") -public final class MockQueryStringParameter { +public class MockQueryStringParameter { - /** - * Query parameter name. - */ private String name; - /** - * Query parameter values. - */ private List values; public String getName() { diff --git a/src/test/java/utils/mocks/model/MockRequest.java b/src/test/java/utils/mocks/model/MockRequest.java index 0cee6ca..1896857 100644 --- a/src/test/java/utils/mocks/model/MockRequest.java +++ b/src/test/java/utils/mocks/model/MockRequest.java @@ -1,6 +1,5 @@ package utils.mocks.model; -import com.beust.jcommander.internal.Lists; import com.google.gson.Gson; import org.mockserver.model.Cookie; import org.mockserver.model.Header; @@ -13,48 +12,30 @@ /** * Concrete definition of a mocked request. - *

+ *
* A mock request is represented by a path and a mapping request (e.g. GET, POST) + *
* Optionally, it also may contain a list of query parameters, a list of cookies, a body and a list of headers - *

+ *
* Each query parameter is represented by {@link MockQueryStringParameter} - *

+ *
* Each cookie is represented by {@link MockCookie} - *

+ *
* Each header is represented by {@link MockHeader} - *

*/ @SuppressWarnings("unused") -public final class MockRequest { +public class MockRequest { - /** - * Mapping request path. - */ private String path; - /** - * Type of mapping request (e.g. GET, POST). - */ private String method; - /** - * List of query parameters. - */ private List queryStringParameters; - /** - * List of cookies. - */ private List cookies; - /** - * Request body. - */ private Object body; - /** - * List of headers. - */ private List headers; public String getPath() { @@ -76,7 +57,7 @@ public void setMethod(String method) { /** * Returns an empty list if the expectation/mock json file has no query parameters defined. * - * @return List of parameters + * @return list of parameters */ public List getQueryStringParameters() { return Optional.ofNullable(queryStringParameters) @@ -93,7 +74,7 @@ public void setQueryStringParameters(List queryStringP /** * Returns an empty list if the expectation/mock json file has no cookies defined. * - * @return List of cookies + * @return list of cookies */ public List getCookies() { return Optional.ofNullable(cookies) @@ -115,7 +96,7 @@ public void setCookies(List cookies) { public String getBody() { return Optional.ofNullable(body) .map(mockBody -> new Gson().toJson(mockBody)) - .orElse("{}"); + .orElse(""); } public void setBody(Object body) { @@ -125,14 +106,14 @@ public void setBody(Object body) { /** * Returns an empty list if the expectation/mock json file has no headers defined. * - * @return List of headers + * @return list of headers */ public List
getHeaders() { return Optional.ofNullable(headers) .map(mockHeaders -> mockHeaders.stream() .map(mockHeader -> new Header(mockHeader.getName(), mockHeader.getValues())) .collect(Collectors.toList())) - .orElse(Lists.newArrayList()); + .orElse(Collections.emptyList()); } public void setHeaders(List headers) { diff --git a/src/test/java/utils/mocks/model/MockResponse.java b/src/test/java/utils/mocks/model/MockResponse.java index f6191cc..aae9ac9 100644 --- a/src/test/java/utils/mocks/model/MockResponse.java +++ b/src/test/java/utils/mocks/model/MockResponse.java @@ -1,6 +1,5 @@ package utils.mocks.model; -import com.beust.jcommander.internal.Lists; import com.google.gson.Gson; import org.mockserver.model.Cookie; import org.mockserver.model.Header; @@ -12,17 +11,17 @@ /** * Concrete definition of a mocked response. - *

+ *
* A mocked response is represented by a body and a status code. + *
* Optionally, it also may contain a list of headers and a list of cookies. - *

+ *
* Each header is represented by {@link MockHeader} - *

+ *
* Each cookie is represented by {@link MockCookie} - *

*/ @SuppressWarnings("unused") -public final class MockResponse { +public class MockResponse { /** * Holds the whole response body. @@ -44,10 +43,15 @@ public final class MockResponse { */ private List cookies; + /** + * Returns an empty body if the expectation/mock json file has no body defined. + * + * @return response body + */ public String getBody() { return Optional.ofNullable(body) .map(mockBody -> new Gson().toJson(mockBody)) - .orElse("{}"); + .orElse(""); } public void setBody(Object body) { @@ -72,7 +76,7 @@ public List
getHeaders() { .map(mockHeaders -> mockHeaders.stream() .map(mockHeader -> new Header(mockHeader.getName(), mockHeader.getValues())) .collect(Collectors.toList())) - .orElse(Lists.newArrayList()); + .orElse(Collections.emptyList()); } public void setHeaders(List headers) { @@ -95,5 +99,4 @@ public List getCookies() { public void setCookies(List cookies) { this.cookies = cookies; } - } diff --git a/src/test/java/utils/reports/ExtentManager.java b/src/test/java/utils/reports/ExtentManager.java index 13ef57c..6d38cb5 100644 --- a/src/test/java/utils/reports/ExtentManager.java +++ b/src/test/java/utils/reports/ExtentManager.java @@ -4,9 +4,6 @@ import com.aventstack.extentreports.ExtentReports; import com.aventstack.extentreports.reporter.ExtentHtmlReporter; -/** - * Singleton ExtentManager. - */ public class ExtentManager { /** @@ -14,6 +11,11 @@ public class ExtentManager { */ private static ExtentReports extentReports; + /** + * Call this method to get the extent report singleton instance. + * + * @return extent reports instance + */ public static synchronized ExtentReports getInstance() { if (extentReports == null) { extentReports = new ExtentReports(); diff --git a/src/test/resources/config/config.local.properties b/src/test/resources/config/config.local.properties deleted file mode 100644 index 7aebf61..0000000 --- a/src/test/resources/config/config.local.properties +++ /dev/null @@ -1,6 +0,0 @@ -browser.type=chrome -base.url=http://google.com -screenshots=false -headless=false -mock.server.url=localhost -mock.server.port=3000 diff --git a/src/test/resources/config/config.qa.properties b/src/test/resources/config/config.qa.properties deleted file mode 100644 index ab922e7..0000000 --- a/src/test/resources/config/config.qa.properties +++ /dev/null @@ -1,7 +0,0 @@ -browser.type=chrome -base.url=http://google.com -screenshots=false -headless=true -remote.webdriver.url=http://localhost:4444/wd/hub -mock.server.url=localhost -mock.server.port=3000 diff --git a/src/test/resources/tests.properties b/src/test/resources/tests.properties deleted file mode 100644 index 480e0eb..0000000 --- a/src/test/resources/tests.properties +++ /dev/null @@ -1 +0,0 @@ -run.tests.local=true diff --git a/testng.xml b/testng.xml index 2d26e93..09c5b70 100644 --- a/testng.xml +++ b/testng.xml @@ -1,10 +1,6 @@ - - - -