Skip to content

Commit

Permalink
revamp current system properties management (#13)
Browse files Browse the repository at this point in the history
  • Loading branch information
Sérgio Martins committed Apr 18, 2020
1 parent bd08e60 commit fa48543
Show file tree
Hide file tree
Showing 36 changed files with 243 additions and 549 deletions.
79 changes: 37 additions & 42 deletions README.md
Original file line number Diff line number Diff line change
@@ -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)
Expand Down Expand Up @@ -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://<<docker_ip>>: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://<<docker_ip>>: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_
Expand All @@ -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=<url> -Dmock.server.port=<port>] \
[-Dparallel=<method>] \
[-DthreadCount=<n>] \
[-Dlistener=<listener1, listener2, ...>]
````

> **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://<<docker_ip>>:4444/wd/hub)|Selenium hub deployed through `docker-compose.yaml`|
|`mock.server.url`|`config/${environment}`|`localhost` (<<docker_ip>>)|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)
Expand Down
30 changes: 24 additions & 6 deletions checkstyle.xml
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,6 @@
<property name="max" value="140"/>
</module>
<module name="TreeWalker">
<module name="JavadocMethod"/>
<module name="JavadocType"/>
<module name="JavadocVariable"/>
<module name="JavadocStyle"/>
<module name="InvalidJavadocPosition"/>
<module name="ConstantName"/>
<module name="LocalFinalVariableName"/>
<module name="LocalVariableName"/>
Expand Down Expand Up @@ -57,8 +52,31 @@
<module name="IllegalInstantiation"/>
<module name="SimplifyBooleanExpression"/>
<module name="SimplifyBooleanReturn"/>
<module name="VisibilityModifier"/>
<module name="ArrayTypeStyle"/>
<module name="UpperEll"/>

<module name="InvalidJavadocPosition"/>
<module name="JavadocTagContinuationIndentation"/>
<module name="SummaryJavadoc">
<property name="forbiddenSummaryFragments"
value="^@return the *|^This method returns |^A [{]@code [a-zA-Z0-9]+[}]( is a )"/>
</module>
<module name="JavadocParagraph"/>
<module name="JavadocMethod">
<property name="scope" value="public"/>
<property name="allowMissingParamTags" value="true"/>
<property name="allowMissingReturnTag" value="true"/>
<property name="allowedAnnotations" value="Override, Test"/>
<property name="tokens" value="METHOD_DEF, CTOR_DEF, ANNOTATION_FIELD_DEF"/>
</module>
<module name="MissingJavadocMethod">
<property name="scope" value="public"/>
<property name="minLineCount" value="2"/>
<property name="allowedAnnotations" value="Override, Test"/>
<property name="tokens" value="METHOD_DEF, CTOR_DEF, ANNOTATION_FIELD_DEF"/>
</module>
<module name="SingleLineJavadoc">
<property name="ignoreInlineTags" value="false"/>
</module>
</module>
</module>
8 changes: 2 additions & 6 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

<groupId>io.github.sergiomartins8</groupId>
<artifactId>ui-automation-bootstrap</artifactId>
<version>1.3.0</version>
<version>1.4.0</version>

<properties>
<maven.compiler.source>1.8</maven.compiler.source>
Expand Down Expand Up @@ -42,13 +42,9 @@
<name>listener</name>
<value>${listener}</value>
</property>
<property>
<name>testnames</name>
<value>${testnames}</value>
</property>
</properties>
<suiteXmlFiles>
<suiteXmlFile>testng.xml</suiteXmlFile> <!--> testng.xml path <-->
<suiteXmlFile>testng.xml</suiteXmlFile>
</suiteXmlFiles>
</configuration>
</plugin>
Expand Down
7 changes: 2 additions & 5 deletions src/test/java/base/Base.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,15 @@

/**
* Bridge between the framework level and the tests.
* <p>
* <br>
* Class' single responsibility is to setup and teardown the webdriver.
* </p>
*/
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
Expand Down
13 changes: 5 additions & 8 deletions src/test/java/base/Browser.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,14 @@

/**
* Holds actions that a user is able to perform on a browser.
* <p>
* <br>
* Browser actions are performed through {@link DriverContext}
* e.g. {@link DriverContext#getBrowser().action()}
* </p>
* <br>
* 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;
Expand Down
16 changes: 0 additions & 16 deletions src/test/java/base/BrowserType.java

This file was deleted.

50 changes: 25 additions & 25 deletions src/test/java/base/DriverContext.java
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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.
* <p>
* Browser actions are performed through {@link DriverContext}
* e.g. {@link DriverContext#getBrowser().action()}
* </p>
* <br>
* 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");
Expand All @@ -60,35 +64,31 @@ 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();
}
}
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();
}
Expand Down
Loading

0 comments on commit fa48543

Please sign in to comment.