diff --git a/src/AxaFrance.WebEngine.Doc/articles/appsettings-java.md b/src/AxaFrance.WebEngine.Doc/articles/appsettings-java.md new file mode 100644 index 0000000..981321a --- /dev/null +++ b/src/AxaFrance.WebEngine.Doc/articles/appsettings-java.md @@ -0,0 +1,53 @@ +# Test Configuration File + +To simplify command line arguments, some parameters can be specified in a configuration file `application.yml`. +If a parameter is provided in both side, the value provided in command-line will be taken account. + +```yaml +webengineConfiguration: + name: myproject-automation + platformName: WINDOWS + browserName: CHROME + browserOptionList: + - --incognito + - --remote-allow-origins=* + appiumConfiguration: + gridConnection: https://hub-cloud.browserstack.com/wd/hub + userName: XXXXXXX + password: XXXXXXX + localTesting: + activate: false + arguments: + force: true + forcelocal: true + binarypath: C:\\BrowserStack\\BrowserStackLocal.exe + localIdentifier: XXXX-YYYY + capabilities: + desiredCapabilitiesMap: + geoLocation: FR + deviceName: Samsung Galaxy S20 Ultra + osVersion: 10.0 + projectName: myproject-automation + buildName: myproject-automation-mobile + sessionName: Samsung + local: true + networkLogs: true + localIdentifier: XXXX-YYYY +applicationConfiguration: + values: + key-1: value-1 + key-2: value-2 + key-3: value-3 +``` + +## General Options +* `name`: The automation project name. +* `platformName`: WINDOWS, ANDROID OR IOS +* `browserName`: CHROMIUM_EDGE, CHROME, FIREFOX, SAFARI. +* `browserOptionList`: Browser option list +* `appiumConfiguration`: Configuration properties for browserstack. Refer to the browserstack documentation +* `localTesting.activate`: Activate or not local testing +* `Capabilities`: To provided necessary Appium capabilities or options for special need. + +> [!NOTE] +> Modifying capabilities may affect the behavior for Web Mobile and App Mobile testing, use it only when it's necessary. diff --git a/src/AxaFrance.WebEngine.Doc/articles/dev-status.md b/src/AxaFrance.WebEngine.Doc/articles/dev-status.md index 1cf6eb9..0a38f60 100644 --- a/src/AxaFrance.WebEngine.Doc/articles/dev-status.md +++ b/src/AxaFrance.WebEngine.Doc/articles/dev-status.md @@ -19,34 +19,34 @@ These actions are available for WebElementDescription. Please note that some act |Action|.NET|Java|Description|Synchronized| |---------|----|----|-----------|--------| -|FindElement()|✔||Finds a unique Web Element with current element description.|✔| -|FindElement(By)|✔| |Finds an sub-element of the current Web Element|✔| -|FindElements()|✔||Finds one or more Web Elements with current element description.(for example options of a `select` element or buttons in the same `radio button group`)|✔| -|FindElements(By)|✔||Finds one or more sub-element of the current unique Web Element|✔| -|Exists()|✔||Checks if an Web Element exists on the DOM|✔| -|Clear()|✔||Clears the value of the element (for text-boxes, text-areas and password-boxes)|✔| -|Click()|✔||Clicks on the Web Element|✔| -|CheckByValue(String)|✔||Checks an option of radio button group based on html attribute `value`. The current description corresponds to all radio buttons of the same group|✔| -|DragAndDropTo(ElementDescription)|✔||Drags the current element and drops to another element. (works only on desktop browser)|✔| -|GetAttribute(string)|✔||Gets the value of the given html attribute|✔| -|GetInnerHtml()|✔||Gets the value of the html attribute `innerHTML`|✔| -|GetOuterHtml()|✔||Gets the value of the html attribute `outerHTML`|✔| -|GetScreenshot()|✔||Generates a screenshot of the current web page|| -|MouseHover()|✔||Hovers the mouse on a given element (Desktop only. on mobile devices, a Click will be performed instead.)|✔| -|RightClick()|✔||Preforms right-click on an element. On Mobile devices, an long touch will be performed|✔| -|ScrollIntoView()|✔||Scrolls the screen until the element is shown in the current view port|| -|SendKeys(string)|✔||Sends the text to textbox based elements (for text-box, text-area and password-box)|✔| -|SetSecure(String)|✔||Takes a crypted data, and set the value to a password-box (works only on password-box)|✔| -|SetValue(string)|✔||Clear the current value of textbox based elements and replace with provided value (for text-box, text-area and password-box)|✔| -|GetText()|✔||Gets the text property of the element (for text-box and text-area)|✔| -|Value|✔||Gets the `value` attribute of the element (usually in html forms)|✔| -|IsSelected|✔||Checks if the Web Element is selected. Applies only on checkboxes, options in a select element and radio buttons|✔| -|IsEnabled|✔||Checks if the Web Element is enabled|| -|IsDisplayed|✔||Checks if the Web Element is visible (Either in viewport or not on mobile devices)|| -|IsVisibleInViewPort|✔||Checks if the Web Element is visible in the current view port|| -|AsSelect()|✔||Converts the Web Element into a `SelectElement`|| -|SelectByIndex(Int32)|✔||Considers current Web Element is `select` and choose an option based on index|✔| -|SelectByText(String)|✔||Considers current Web Element is `select` and choose an option based on displayed text|✔| -|SelectByValue(String)|✔||Considers current Web Element is `select` and choose an option based on `value` attribute|✔| +|FindElement()|✔|✔|Finds a unique Web Element with current element description.|✔| +|FindElement(By)|✔|✔ |Finds an sub-element of the current Web Element|✔| +|FindElements()|✔|✔|Finds one or more Web Elements with current element description.(for example options of a `select` element or buttons in the same `radio button group`)|✔| +|FindElements(By)|✔|✔|Finds one or more sub-element of the current unique Web Element|✔| +|Exists()|✔|✔|Checks if an Web Element exists on the DOM|✔| +|Clear()|✔|✔|Clears the value of the element (for text-boxes, text-areas and password-boxes)|✔| +|Click()|✔|✔|Clicks on the Web Element|✔| +|CheckByValue(String)|✔|✔|Checks an option of radio button group based on html attribute `value`. The current description corresponds to all radio buttons of the same group|✔| +|DragAndDropTo(ElementDescription)|✔|✔|Drags the current element and drops to another element. (works only on desktop browser)|✔| +|GetAttribute(string)|✔|✔|Gets the value of the given html attribute|✔| +|GetInnerHtml()|✔|✔|Gets the value of the html attribute `innerHTML`|✔| +|GetOuterHtml()|✔|✔|Gets the value of the html attribute `outerHTML`|✔| +|GetScreenshot()|✔|✔|Generates a screenshot of the current web page|| +|MouseHover()|✔|✔|Hovers the mouse on a given element (Desktop only. on mobile devices, a Click will be performed instead.)|✔| +|RightClick()|✔|✔|Preforms right-click on an element. On Mobile devices, an long touch will be performed|✔| +|ScrollIntoView()|✔|✔|Scrolls the screen until the element is shown in the current view port|| +|SendKeys(string)|✔|✔|Sends the text to textbox based elements (for text-box, text-area and password-box)|✔| +|SetSecure(String)|✔|✔|Takes a crypted data, and set the value to a password-box (works only on password-box)|✔| +|SetValue(string)|✔|✔|Clear the current value of textbox based elements and replace with provided value (for text-box, text-area and password-box)|✔| +|GetText()|✔|✔|Gets the text property of the element (for text-box and text-area)|✔| +|Value|✔|✔|Gets the `value` attribute of the element (usually in html forms)|✔| +|IsSelected|✔|✔|Checks if the Web Element is selected. Applies only on checkboxes, options in a select element and radio buttons|✔| +|IsEnabled|✔|✔|Checks if the Web Element is enabled|| +|IsDisplayed|✔|✔|Checks if the Web Element is visible (Either in viewport or not on mobile devices)|| +|IsVisibleInViewPort|✔|✔|Checks if the Web Element is visible in the current view port|| +|AsSelect()|✔|✔|Converts the Web Element into a `SelectElement`|| +|SelectByIndex(Int32)|✔|✔|Considers current Web Element is `select` and choose an option based on index|✔| +|SelectByText(String)|✔|✔|Considers current Web Element is `select` and choose an option based on displayed text|✔| +|SelectByValue(String)|✔|✔|Considers current Web Element is `select` and choose an option based on `value` attribute|✔| diff --git a/src/AxaFrance.WebEngine.Doc/articles/report-viewer.md b/src/AxaFrance.WebEngine.Doc/articles/report-viewer.md index fa168e8..7603f15 100644 --- a/src/AxaFrance.WebEngine.Doc/articles/report-viewer.md +++ b/src/AxaFrance.WebEngine.Doc/articles/report-viewer.md @@ -3,8 +3,14 @@ ReportViewer is an application to view test results in a graphical way. As shown as follow, Test cases executed have been listed on the left side. Once a test case is selected, detailed test steps, test data, screenshot and other necessary information will be shown on the right side. +With thick client : + ![Report Viewer](../images/report-viewer.png) +With thin client : + +![Html report viewer](../images/img-report-html.png) + ## When report is generated? Each test execution via `WebRunner.exe` or `webrunner.jar` generates a test report in the given directory provided by `-outputDir` parameter. diff --git a/src/AxaFrance.WebEngine.Doc/articles/webengine-web.md b/src/AxaFrance.WebEngine.Doc/articles/webengine-web.md index 1350c38..ff8a957 100644 --- a/src/AxaFrance.WebEngine.Doc/articles/webengine-web.md +++ b/src/AxaFrance.WebEngine.Doc/articles/webengine-web.md @@ -189,7 +189,7 @@ else LoginPage page = new LoginPage(driver); page.getTxtUsername().setValue("admin@test.com"); //fill username page.getTxtPassword().sendKeys("password"); //fill password -page.getButtonSubmit().Click(); +page.getButtonSubmit().click(); if (page.getSpanErrorMessage().isDisplayed()){ //check if error message shows //error message displayed, test failed. diff --git a/src/AxaFrance.WebEngine.Doc/articles/webrunner.md b/src/AxaFrance.WebEngine.Doc/articles/webrunner.md index 05f0cef..9de5258 100644 --- a/src/AxaFrance.WebEngine.Doc/articles/webrunner.md +++ b/src/AxaFrance.WebEngine.Doc/articles/webrunner.md @@ -12,13 +12,13 @@ webrunner "-a:" [-data:] [-env:] [-browser:< ``` # [Java](#tab/java) ```batch -java -jar webrunner.jar "-a:" [-data:] [-env:] [-browser:] [optional_arguments] +java -jar MyProject.jar [-data:] [-env:] [-browser:] [optional_arguments] ``` *** ## Prerequisites To run tests build with WebEngine Framework, you have to provide compiled test automation solution. -* `-a:`: the filename or full path of compiled test project: DLL library for .NET or a JAR package for Java. +* `-a:`: the filename or full path of compiled test project: DLL library for .NET. If your solution use Data-Driven approach, it is required to provide at least one of following files: * `-data:`: Test Data to be used for data-driven test execution, in XML format. The file can be exported via `Excel Add-in` @@ -36,7 +36,7 @@ WebRunner.exe "-a:MyProject.dll" "-data:Data.xml" "-env:Staging.xml" "-browser:F # [Java](#tab/java) ```batch -java -jar webrunner.jar "-a:project.jar" "-data:Data.xml" "-env:Staging.xml" "-browser:Firefox" "-outputDir:C:\Temp" +java -jar MyProject.jar "-data:Data.xml" "-env:Staging.xml" "-browser:Firefox" "-outputDir:C:\Temp" ``` *** @@ -51,7 +51,7 @@ WebRunner.exe "-a:MyProject.dll" "-data:Data.xml" "-env:Env.xml" "-platform:Andr # [Java](#tab/java) ```batch -java -jar webrunner.jar "-a:MyProject.jar" "-data:Data.xml" "-env:Env.xml" "-platform:Android" "-browser:Chrome" "-device:Emulator" "-outputDir:C:\Temp" +java -jar MyProject.jar "-data:Data.xml" "-env:Env.xml" "-platform:Android" "-browser:Chrome" "-device:Emulator" "-outputDir:C:\Temp" ``` *** @@ -65,7 +65,11 @@ WebRunner.exe "-a:AppProject.dll" "-data:Data.xml" "-env:Staging.xml" -platform: ## Optional parameters > [!NOTE] -> Some parameters can be provided in the configuration file `appsettings.json` for C# and `application-properties.yml` for JAVA. please refer to [Test Configuration](appsettings.md) +> Some parameters can be provided in the configuration file `appsettings.json` for C# and `application-properties.yml` for JAVA. + +> Please refer to [Test Configuration C#](appsettings.md) for C# + +> Please refer to [Test Configuration JAVA](appsettings-java.md) for Java ### Commun parameters #### -browser:\ @@ -73,7 +77,7 @@ Specifies the browser on which to run test. see: Specifies The platform for test execution: see: . Default value is `Windows`. #### -outputDir:\ -Specifies the folder to store output of test execution and test report. This parameter is can be defined in `appsetting.json` for C# and `application-properties.yml` for JAVA. +Specifies the folder to store output of test execution and test report. This parameter is can be defined in `appsetting.json` for C# and `application.yml` for JAVA. #### -m Specifies the manual debug mode. Use this mode for debugging test scenarios locally. When the test is failed test will pause for manual intervention before clean-up process. diff --git a/src/AxaFrance.WebEngine.Doc/images/dd-excel-param-java.png b/src/AxaFrance.WebEngine.Doc/images/dd-excel-param-java.png new file mode 100644 index 0000000..3beb76a Binary files /dev/null and b/src/AxaFrance.WebEngine.Doc/images/dd-excel-param-java.png differ diff --git a/src/AxaFrance.WebEngine.Doc/images/dd-exceldata-java.png b/src/AxaFrance.WebEngine.Doc/images/dd-exceldata-java.png new file mode 100644 index 0000000..0677c52 Binary files /dev/null and b/src/AxaFrance.WebEngine.Doc/images/dd-exceldata-java.png differ diff --git a/src/AxaFrance.WebEngine.Doc/images/dd-excelenv-java.png b/src/AxaFrance.WebEngine.Doc/images/dd-excelenv-java.png new file mode 100644 index 0000000..5fa4d0c Binary files /dev/null and b/src/AxaFrance.WebEngine.Doc/images/dd-excelenv-java.png differ diff --git a/src/AxaFrance.WebEngine.Doc/images/dd-step1-java.png b/src/AxaFrance.WebEngine.Doc/images/dd-step1-java.png new file mode 100644 index 0000000..393956e Binary files /dev/null and b/src/AxaFrance.WebEngine.Doc/images/dd-step1-java.png differ diff --git a/src/AxaFrance.WebEngine.Doc/images/dd-step4-java.png b/src/AxaFrance.WebEngine.Doc/images/dd-step4-java.png new file mode 100644 index 0000000..7ebb725 Binary files /dev/null and b/src/AxaFrance.WebEngine.Doc/images/dd-step4-java.png differ diff --git a/src/AxaFrance.WebEngine.Doc/images/export-test-data-and-env.png b/src/AxaFrance.WebEngine.Doc/images/export-test-data-and-env.png new file mode 100644 index 0000000..7bbe758 Binary files /dev/null and b/src/AxaFrance.WebEngine.Doc/images/export-test-data-and-env.png differ diff --git a/src/AxaFrance.WebEngine.Doc/images/img-report-html.png b/src/AxaFrance.WebEngine.Doc/images/img-report-html.png new file mode 100644 index 0000000..aafaa4a Binary files /dev/null and b/src/AxaFrance.WebEngine.Doc/images/img-report-html.png differ diff --git a/src/AxaFrance.WebEngine.Doc/images/java/data-driven/result-data-driven.png b/src/AxaFrance.WebEngine.Doc/images/java/data-driven/result-data-driven.png new file mode 100644 index 0000000..68a2877 Binary files /dev/null and b/src/AxaFrance.WebEngine.Doc/images/java/data-driven/result-data-driven.png differ diff --git a/src/AxaFrance.WebEngine.Doc/images/java/data-driven/run-data-driven-from-excel.png b/src/AxaFrance.WebEngine.Doc/images/java/data-driven/run-data-driven-from-excel.png new file mode 100644 index 0000000..8b407ef Binary files /dev/null and b/src/AxaFrance.WebEngine.Doc/images/java/data-driven/run-data-driven-from-excel.png differ diff --git a/src/AxaFrance.WebEngine.Doc/images/java/data-driven/run-data-driven.png b/src/AxaFrance.WebEngine.Doc/images/java/data-driven/run-data-driven.png new file mode 100644 index 0000000..ad19033 Binary files /dev/null and b/src/AxaFrance.WebEngine.Doc/images/java/data-driven/run-data-driven.png differ diff --git a/src/AxaFrance.WebEngine.Doc/images/java/data-driven/test-suite-and-env.png b/src/AxaFrance.WebEngine.Doc/images/java/data-driven/test-suite-and-env.png new file mode 100644 index 0000000..69fe228 Binary files /dev/null and b/src/AxaFrance.WebEngine.Doc/images/java/data-driven/test-suite-and-env.png differ diff --git a/src/AxaFrance.WebEngine.Doc/images/java/gherkin/generate-java-class-from-feature.png b/src/AxaFrance.WebEngine.Doc/images/java/gherkin/generate-java-class-from-feature.png new file mode 100644 index 0000000..db06dda Binary files /dev/null and b/src/AxaFrance.WebEngine.Doc/images/java/gherkin/generate-java-class-from-feature.png differ diff --git a/src/AxaFrance.WebEngine.Doc/images/java/gherkin/gherkin-project-structure.png b/src/AxaFrance.WebEngine.Doc/images/java/gherkin/gherkin-project-structure.png new file mode 100644 index 0000000..02e3cf3 Binary files /dev/null and b/src/AxaFrance.WebEngine.Doc/images/java/gherkin/gherkin-project-structure.png differ diff --git a/src/AxaFrance.WebEngine.Doc/images/java/gherkin/java-class-feature-code.png b/src/AxaFrance.WebEngine.Doc/images/java/gherkin/java-class-feature-code.png new file mode 100644 index 0000000..f810eb4 Binary files /dev/null and b/src/AxaFrance.WebEngine.Doc/images/java/gherkin/java-class-feature-code.png differ diff --git a/src/AxaFrance.WebEngine.Doc/images/java/gherkin/java-class-feature-name.png b/src/AxaFrance.WebEngine.Doc/images/java/gherkin/java-class-feature-name.png new file mode 100644 index 0000000..a7b0e3d Binary files /dev/null and b/src/AxaFrance.WebEngine.Doc/images/java/gherkin/java-class-feature-name.png differ diff --git a/src/AxaFrance.WebEngine.Doc/images/java/gherkin/result-test-case.png b/src/AxaFrance.WebEngine.Doc/images/java/gherkin/result-test-case.png new file mode 100644 index 0000000..7317c2e Binary files /dev/null and b/src/AxaFrance.WebEngine.Doc/images/java/gherkin/result-test-case.png differ diff --git a/src/AxaFrance.WebEngine.Doc/images/java/gherkin/run-test-cases.png b/src/AxaFrance.WebEngine.Doc/images/java/gherkin/run-test-cases.png new file mode 100644 index 0000000..b95dadb Binary files /dev/null and b/src/AxaFrance.WebEngine.Doc/images/java/gherkin/run-test-cases.png differ diff --git a/src/AxaFrance.WebEngine.Doc/images/java/keyword-driven/keyword-driven-project-structure.png b/src/AxaFrance.WebEngine.Doc/images/java/keyword-driven/keyword-driven-project-structure.png new file mode 100644 index 0000000..109ea86 Binary files /dev/null and b/src/AxaFrance.WebEngine.Doc/images/java/keyword-driven/keyword-driven-project-structure.png differ diff --git a/src/AxaFrance.WebEngine.Doc/images/java/keyword-driven/result-keyword-driven-test.png b/src/AxaFrance.WebEngine.Doc/images/java/keyword-driven/result-keyword-driven-test.png new file mode 100644 index 0000000..80d1a73 Binary files /dev/null and b/src/AxaFrance.WebEngine.Doc/images/java/keyword-driven/result-keyword-driven-test.png differ diff --git a/src/AxaFrance.WebEngine.Doc/images/java/linear/linear-result.png b/src/AxaFrance.WebEngine.Doc/images/java/linear/linear-result.png new file mode 100644 index 0000000..4caf462 Binary files /dev/null and b/src/AxaFrance.WebEngine.Doc/images/java/linear/linear-result.png differ diff --git a/src/AxaFrance.WebEngine.Doc/images/java/linear/run-linear-test.png b/src/AxaFrance.WebEngine.Doc/images/java/linear/run-linear-test.png new file mode 100644 index 0000000..1e62a81 Binary files /dev/null and b/src/AxaFrance.WebEngine.Doc/images/java/linear/run-linear-test.png differ diff --git a/src/AxaFrance.WebEngine.Doc/images/search-prospect.png b/src/AxaFrance.WebEngine.Doc/images/search-prospect.png new file mode 100644 index 0000000..ea4e59a Binary files /dev/null and b/src/AxaFrance.WebEngine.Doc/images/search-prospect.png differ diff --git a/src/AxaFrance.WebEngine.Doc/tutorials/data-driven-java.md b/src/AxaFrance.WebEngine.Doc/tutorials/data-driven-java.md index c955640..5e0114b 100644 --- a/src/AxaFrance.WebEngine.Doc/tutorials/data-driven-java.md +++ b/src/AxaFrance.WebEngine.Doc/tutorials/data-driven-java.md @@ -1 +1,344 @@ -![Toc Workaround](toc-workaround.md)# Tutorials +# Data-Driven testing Approach (Java/Spring boot) + +This article will show steps to build an Automation solution based on Data-Driven testing approach with `Externalized test data` and `Dynamic execution based on test data`. +We will continue from the previous test project for application http://webengine-test.azurewebsites.net/home-insurance. + +# Prerequisite: +* Familiar with [Keyword-Driven testing approach](keyworddriven.md) +* Have already followed the tutorial [Keyword-Driven testing Approach (Java/Spring boot)](keyword-driven-java.md) and have a working project. + +# Step 1: Review modulization, project structure and keywords +The modelling of Home Insurance Underwriting application may look like following diagram: + +![Schemas](../images/kd-schemas.png) + +> [!NOTE] +> This tutorial is based on the outcome of [Keyword-Driven testing Approach (Java/Spring boot)](keyword-driven-java.md). If you don't have a working keyword-driven test project yet, please follow that article first. + +# Step 2: Identify variables +In this step, we'll need to identify variables used in the test automation solution. +To record and manage test data, the most convenient way is to use an Excel spreadsheet under WebEngine format. +So you can run all your tests directly from Excel with WebEngine Excel Add-in. + +For more information about Excel test data +* [Excel Add-in](../articles/excel-addin.md) +* [Download Excel test data for this tutorial](../files/Data-HomeInsurance.xlsx) + +Test data will have 3 sheets: +* `PARAMS`: All the possible parameters of test case and it's description. +* `ENV`: Test environment dependent variables such as URL or the site or the name of the server. +* `TEST_SUITE`: represents the test suite (including a list of test cases) + + +For this test, we developed following test data +# [PARAMS](#tab/param) +Describes all the test parameters + +![](../images/dd-excel-param-java.png) + + + +# [ENV](#tab/env) +Here we lists all test environment dependent variables such as URLs. + +If you have more than one test environment, you can list all data with prefix or postfix + +![](../images/dd-excelenv-java.png) + + +# [TEST_SUITE](#tab/testsuite) +In test data we will specify the test cases and test data used for each test case. + +For example: our test suite will have 3 test cases, covers Apartment, House and Apartment with previous accident. +From spreadsheet we can clearly see how parameters are used and have an idea about the test coverage of each parameter. +To increase test coverage, we can simply create new columns without the need to modify the test script. + +![](../images/dd-exceldata-java.png) +*** + +> [!NOTE] +> There is no need to develop every parameter and the variable for every test case. +> The solution can be improved with the time by increasing test coverage in width (by increasing test cases) and in depth (by increasing test parameters) + +# Step 3: Using test parameters in the script. +## 3.1: Import test parameters from EXCEL +The function of WebEngine Addin -> Tools -> Code Generation can generate a Java class `ParameterList` for you, including all test parameters with their descriptions as comment + +![Excel Code Generation](../images/excel-code-generation.png) + +# [Why use ParameterList](#tab/why-parameter-list) +Using parameter list are following advantage: +* Benefit from code auto-completion from development tools. +* Understands the meaning of the parameter thanks to the comments. +* Avoid type error in the test script. + +# [Content of ParameterList](#tab/content-parameter-list) +You should split after into two classes for maintenability + +```java +package fr.axa.automation.parameter; + +public class EnvironnementVariable { + public static final String URL = "URL"; + public static final String LOGIN = "LOGIN"; + public static final String PASSWORD = "PASSWORD"; +} +``` + +```java +package fr.axa.automation.parameter; + +public class DataVariable { + public static final String PROSPECT_NAME = "Nicolas"; +} +``` +*** + +## 3.2: Use variables in the script +In actions, you can use `EnvironnementVariable.URL` contant to retrieve the URL value of an Environment Variable and use `DataVariable.PROSPECT_NAME` constant to retrieve test data. + +For Example, compared to the hard-coded login action, now the keyword action `Login` looks like following code snippet: + +* We get the parameter `Environment` from test data +* Get `URL` from Environment Variables. +* Fill `username` and `password` with the value from parameter `Username` and `Password` + This action can do whatever needed according to the test data provided. + +# [Login.java (Hard Coded)](#tab/login-hc) +```java +package fr.axa.automation.action; + +import fr.axa.automation.model.LoginModel; +import fr.axa.automation.parameter.EnvironnementVariable; +import fr.axa.automation.webengine.core.AbstractActionWebBase; +import lombok.AccessLevel; +import lombok.experimental.FieldDefaults; + +@FieldDefaults(level = AccessLevel.PRIVATE) +public class LoginAction extends AbstractActionWebBase { + LoginModel loginModel; + + public LoginAction() { + } + + @Override + public void doAction() throws Exception { + loginModel = new LoginModel(getWebDriver()); + getWebDriver().get("http://webengine-test.azurewebsites.net/home-insurance/"); + loginModel.getLogin().sendKeys("login"); + loginModel.getPassword().sendKeys("pwd"); + screenShot(); + loginModel.getButton().click(); + addInformation("First step succeed"); + } + + @Override + public boolean doCheckpoint() throws Exception { + return true; + } +} +``` +# [Login.java (Data Driven)](#tab/login-dd) +```java +package fr.axa.automation.action; + +import fr.axa.automation.model.LoginModel; +import fr.axa.automation.parameter.EnvironnementVariable; +import fr.axa.automation.webengine.core.AbstractActionWebBase; +import lombok.AccessLevel; +import lombok.experimental.FieldDefaults; + +@FieldDefaults(level = AccessLevel.PRIVATE) +public class LoginAction extends AbstractActionWebBase { + LoginModel loginModel; + + public LoginAction() { + } + + @Override + public void doAction() throws Exception { + + String url = getEnvironnementValueWithException(EnvironnementVariable.URL); // URL Define in env.xml file + String login = getEnvironnementValueWithException(EnvironnementVariable.LOGIN); // URL Define in env.xml file + String pwd = getEnvironnementValueWithException(EnvironnementVariable.PASSWORD); // URL Define in env.xml file + loginModel = new LoginModel(getWebDriver()); + getWebDriver().get(url); + loginModel.getLogin().sendKeys(login); + loginModel.getPassword().sendKeys(pwd); + screenShot(); + loginModel.getButton().click(); + addInformation("First step succeed"); + } + + @Override + public boolean doCheckpoint() throws Exception { + return true; + } +} +``` +*** + +# Step 4: Data-Driven test script + +Repeat the step 3.2 on every keyword action, you can completely remove hard coded test data. +But sometimes it's not enough, because test procedure may change as test data changes. + +## 4.1 Manages test process driven by data +In our application `home type` can be `apartment` or `house`, according to its value. +The forms will be different. + +Our script must implement this logic driven by test data data: when the type is `apartment`, the script will fill form for apartments, otherwise the script will fill form for houses. + +This logic is implemented in the keyword action `HomeAction`, according to the value of home type, different sub-action will be executed. +package fr.axa.automation.action; + +```java +package fr.axa.automation.action; + +import fr.axa.automation.model.HomeDetailModel; +import fr.axa.automation.parameter.DataVariable; +import fr.axa.automation.webengine.core.AbstractActionWebBase; +import lombok.AccessLevel; +import lombok.experimental.FieldDefaults; + +@FieldDefaults(level = AccessLevel.PRIVATE) +public class homeDetailTypeAppartmentAction extends AbstractActionWebBase { + HomeDetailModel homeDetailModel; + + public homeDetailTypeAppartmentAction() { + } + + @Override + public void doAction() throws Exception { + String totalFloor = getParameterWithException(DataVariable.TOTAL_FLOOR); + String floorNumber = getParameterWithException(DataVariable.FLOOR_NUMBER); + homeDetailModel = new HomeDetailModel(getWebDriver()); + homeDetailModel.getTypeHomeAppartment().click(); + homeDetailModel.getTotalFloor().selectByValue(totalFloor); + homeDetailModel.getFloorNumber().sendKeys(floorNumber); + screenShot(); + addInformation("Home subscription step succeed"); + } + + @Override + public boolean doCheckpoint() throws Exception { + return true; + } +} +``` + + +## 4.3 Resume +With above technics, you can control the test process or optional actions by external data. +That means if every keyword action is implemented like above examples, +you can run tests with any combination of data without the need to modify and update the code. + +In general, simple controls can be implemented with `if` statement. but when the flow control becomes complex, it is recommended to separate these logics into sub-actions to keep an action at a reasonable complexity. + +# Step 5: Test suite driven by data +We have improved every keyword action, and technically we can run test case with any combination of test data. + +# [Test Suite ](#tab/ts-code) + +```java +package fr.axa.automation.testsuite; + +import fr.axa.automation.testcase.SubscriptionTypeAppartmentTestCase; +import fr.axa.automation.webengine.core.AbstractTestSuite; +import fr.axa.automation.webengine.core.ITestCase; + +import java.util.AbstractMap; +import java.util.ArrayList; +import java.util.List; + +public class TestSuite extends AbstractTestSuite { + + public static final String SUBSCRIPTION_HOME = "SUBSCRIPTION_HOME"; //Test case defined in data.xml + + @Override + public List> getTestCaseList() { + List> testCaseList = new ArrayList(); + testCaseList.add(new AbstractMap.SimpleEntry(SUBSCRIPTION_HOME,new SubscriptionTypeAppartmentTestCase())); + return testCaseList; + } +} +``` +*** + +# Step 6: Export test data +Now go back to Excel test data and export Test Data and Environment Variables. +Save them in the resource directory. + +![](../images/export-test-data-and-env.png) + +![](../images/java/data-driven/test-suite-and-env.png) + + +# Step 7: Debug and Execute test cases +Similar to previous article, you'll need to configure the project properties to launch the Application class with appropriate parameters. + + +![](../images/java/data-driven/run-data-driven.png) + + +This time, we will provide 2 more parameters: +* `-data:` to provide Test Data +* `-env:` to provide Environment Variables + +Now we are good to go. + +Launch your project and now we can see the test is running until the test report is showing: + +![](../images/java/data-driven/result-data-driven.png) + +If error happens in the test script, you can set breakpoint in the code and debug the script line by line. + +# Step 8: Run tests directly from EXCEL +It is also possible to run tests directly from EXCEL. The advantage is that you can run one or more tests via selection. +But before launch the test via EXCEL, you must tell WebEngine Add-in where your automate solution is located. + +This can be configured in Settings: +`WebEngine` -> `Settings` + +![](../images/java/data-driven/run-data-driven-from-excel.png) + +* **Export Directory**: The folder where Test data and Environment variables should be exported. +* **WebRunner Directory**: The folder where `webrunner.exe` or `webrunner.jar` is located along with your test solution. + In general, the output folder of your test projet in `bin\\debug` +* **Test Assembly**: The compiled library contains your test script. By default, its name is your `.dll` for C# projet and `.jar` for Java. + +Once the settings is done, now we can run any and any number of tests directly: +For example: run `TEST_02`: + +# [Select Test](#tab/run-step-1) + +Select `SUBSCRIPTION_HOME` cell + +![](../images/dd-step1-java.png) +# [Launch Test](#tab/run-step-2) + +Click `Launch Test` +![Dd Step2](../images/dd-step2.png) + +# [Select Browser](#tab/run-step-3) + +Choose a desktop browser, for example `Firefox`. Then click `Start` +![Dd Step3](../images/dd-step3.png) + +# [Observe Test Execution](#tab/run-step-4) + +Now you can see the framework is running Test_02 on Firefox: +![](../images/dd-step4-java.png) +*** + +> [!NOTE] +> By running tests directly from Excel, Webrunner is not attached with Visual Studio debugger. So it's not possible to debug the code line by line. +> If you want to debug a particular test case or action, you can use Excel Add-in to export that particular test case, then launch the project with-in Visual Studio. + +# Conclusion +Congratulations! You've reached here and have a dynamic data-driven test solution for your application. + +Now you can study test coverage and develop other test cases, if necessary, in both directions: +* In Width: to develop additional test cases with new combination of test data. + This can be done exclusively within Excel without the need to motify the code or the test project. +* In Depth: If we want to do more verifications or to cover more functionalities. we'll need to update appropriate keyword-actions or add new keyword action, then externalize test data for these newly added codes. diff --git a/src/AxaFrance.WebEngine.Doc/tutorials/gherkin-java.md b/src/AxaFrance.WebEngine.Doc/tutorials/gherkin-java.md index c955640..1693646 100644 --- a/src/AxaFrance.WebEngine.Doc/tutorials/gherkin-java.md +++ b/src/AxaFrance.WebEngine.Doc/tutorials/gherkin-java.md @@ -1 +1,281 @@ -![Toc Workaround](toc-workaround.md)# Tutorials +# Writing test cases - Gherkin Approach (Java / Spring boot) +In this article we will show you how to write test cases using Gherkin approach. +Gherkin approach is often used to test features in Behavior-Driven Development (for example: acceptance criteria of a user story) and in Agile environment. The test cases are represented by test scenarios written in gherkin language, and each sentence is implemented by code-behind. You can reuse the same sentence across the test project so the code-behind can also be reused. + +With this method, testers (or business users) and automation engineers can work together: One can maintain the repository of test scenarios in Gherkin and the other can maintain automation implementations. When a scenario is not yet automated, it can still be served for manual execution. + +> [!NOTE] +> Gherkin approach is perfect for feature testing combined with BDD (Behavior-Driven Development). +> +> But it may not be suitable for complex end-to-end tests. For this use cases, Gherkin scenarios may be too long, and the code-behind of each phrase may be difficult to maintain. +> For complex end-to-end tests, you may consider Keyword-driven/Data-driven testing approach. + + +### Step 1: Prerequisites for Gherkin Approach + +JDK 8, maven and lombok + +### Step 2: Create a simple maven project +Open pom.xml paste below code + +```xml + + + + 4.0.0 + + fr.axa.automation.webengine + sample-gherkin-webengine-java + 1.0.0-SNAPSHOT + jar + sample-gherkin-webengine-java + + + 3.10.1 + 3.0.0-M7 + 2.2.0 + + + + + + fr.axa.automation.webengine + webengine-boot-gherkin + ${webengine-boot-gherkin.version} + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven-compiler-plugin.version} + + + + org.apache.maven.plugins + maven-surefire-plugin + ${maven-surefire-plugin.version} + + + + + +``` + +For your information, we use junit 5 with the framework + +### Step 3: Project structure +![](../images/java/gherkin/gherkin-project-structure.png) + +* In `Features` folder, test scenarios written in Gherkin Language and saved in `.features` file. +* In `step`, the implementation of these scenarios by code. +* In `model`, the page model. +* CucumberRunnerTest : Runner for executing tests + + + +### Step 4: Observe SUT and identify UI Elements +Observing system under test from Developer tools provided with browser. Here in our tutorial, we will operate 3 elements, to benefits the advantages of the Framework, we will put these 3 elements into a `PageModel`. + +![Step 3 UI Elements](../images/ls-step3-uielements.png) + +This is exactly the same step we've discussed in Linear Script Approach. Please refer to +[Observe SUT and Identify UI Elements](linear-script-cs.md#step-3-observe-sut-and-identify-ui-elements) for more information. + +### Step 5: Write test scenarios (.feature) +Create a Feature file and name it `sample-flow.feature`, place it under the folder `features` +Paste this below code : + +```feature +Feature: Sample training + + Scenario: first-scenario + Given I visit the test page + When I choose the language with text "Français" + And I want to buy a coffee + And I click on the first next button + Then I go to the next page +``` + +There is no limit how to write the scenario using the keywords given-when-then. But generally, people often use `Given` to specify the preconditions, `When` to specify actions and `Then` to specify expected results. +Following the same naming convention across teams can ensure good understanding of scenarios for all stakeholders. + +### Step 6: Generate test steps from features +Once the feature is written, place the caret at a step in your .feature file and press Alt + Enter. + +![](../images/java/gherkin/generate-java-class-from-feature.png) + +Name the java class: SampleFlowStep.java +![](../images/java/gherkin/java-class-feature-name.png) + +It will automatically generate code to match each sentence: + +```java +package fr.axa.automation.feature.step; + +import io.cucumber.java.en.And; +import io.cucumber.java.en.Given; +import io.cucumber.java.en.Then; +import io.cucumber.java.en.When; + +public class SampleFlowStep { +@Given("I visit the test page") +public void iVisitTheTestPage() { +} + + @When("I choose the language with text {string}") + public void iChooseTheLanguageWithText(String arg0) { + } + + @And("I want to buy a coffee") + public void iWantToBuyACoffee() { + } + + @And("I click on the first next button") + public void iClickOnTheFirstNextButton() { + } + + @Then("I go to the next page") + public void iGoToTheNextPage() { + } +} +``` + + +### Step 5: Complete the code for using page model and browser factory + +Create the page model : + +```java +package fr.axa.automation.feature.model; + +import fr.axa.automation.webengine.core.AbstractPageModel; +import fr.axa.automation.webengine.core.WebElementDescription; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.experimental.FieldDefaults; +import org.openqa.selenium.WebDriver; + + +@FieldDefaults(level = AccessLevel.PUBLIC) +public class FirstPageModel extends AbstractPageModel { + + @Getter + WebElementDescription language = WebElementDescription.builder().tagName("select").id("language").build(); + + @Getter + WebElementDescription coffeeRadio = WebElementDescription.builder().tagName("input").id("coffee").build(); + + @Getter + WebElementDescription teaRadio = WebElementDescription.builder().tagName("input").id("tea").build(); + + @Getter + WebElementDescription waterRadio = WebElementDescription.builder().tagName("input").id("water").build(); + + @Getter + WebElementDescription nextStep = WebElementDescription.builder().tagName("button").xPath(".//button[contains(text(),\"Next (3-second-delay)\")]").build(); + + public FirstPageModel(WebDriver webDriver) throws Exception { + populateDriver(webDriver); + } +} +``` + +Complete the SampleFlowStep class + +```java +package fr.axa.automation.feature.step; + +import fr.axa.automation.feature.model.FirstPageModel; +import fr.axa.automation.webengine.helper.WebdriverHelper; +import fr.axa.automation.webengine.step.AbstractStep; +import io.cucumber.java.en.And; +import io.cucumber.java.en.Given; +import io.cucumber.java.en.Then; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.experimental.FieldDefaults; +import org.openqa.selenium.WebDriver; + +@Getter +@FieldDefaults(level = AccessLevel.PROTECTED) +public class SampleFlowStep extends AbstractStep { + + WebDriver driver; + FirstPageModel firstPageModel; + + public SampleFlowStep() throws Exception { + driver = WebdriverHelper.initializeDriver(); + firstPageModel = new FirstPageModel(driver); + } + + @Given("^I visit the test page$") + public void visitTheTestPage() throws InterruptedException { + addInformation("Open WebEngine test page"); + driver.get("http://webengine-test.azurewebsites.net/Step1.html"); + } + + @And("^I choose the language with text \"([^\"]*)\"$") + public void chooseTheLanguage(String language) throws Exception { + addInformation("Choose the language"); + getFirstPageModel().getLanguage().selectByText(language); + } + + @And("^I want to buy a coffee$") + public void seePopUpAndEnterText() throws Exception { + getFirstPageModel().getCoffeeRadio().click(); + } + + @And("^I click on the first next button$") + public void clickFirstButtonOKInThePopup() throws Exception { + getFirstPageModel().getNextStep().click(); + } + + @Then("^I go to the next page$") + public void goToTheNextPage() { + addInformation("Success"); + getDriver().close(); + } +} +``` + +### Step 7: Create the runner class +``` java +package fr.axa.automation.feature; + +import org.junit.platform.suite.api.ConfigurationParameter; +import org.junit.platform.suite.api.ConfigurationParameters; +import org.junit.platform.suite.api.IncludeEngines; +import org.junit.platform.suite.api.SelectClasspathResource; +import org.junit.platform.suite.api.Suite; + +import static io.cucumber.junit.platform.engine.Constants.GLUE_PROPERTY_NAME; +import static io.cucumber.junit.platform.engine.Constants.PLUGIN_PROPERTY_NAME; + +@Suite +@IncludeEngines("cucumber") +@SelectClasspathResource("features") +@ConfigurationParameters({ + @ConfigurationParameter(key = GLUE_PROPERTY_NAME, value = "fr.axa.automation.feature.step"), + @ConfigurationParameter(key = PLUGIN_PROPERTY_NAME, value = "pretty, html:target/report-gherkin/report.html, fr.axa.automation.webengine.listener.WebengineReportListener, json:target/cucumber-report/cucumber.json") + }) +public class CucumberRunnerTest { + +} +``` + +### Step 6: Run test case +Run : +![](../images/java/gherkin/run-test-cases.png) + +Result : +![](../images/java/gherkin/result-test-case.png) + +> [!NOTE] +> The source code of this exercise can be found in our Github repository. +> Project Name: sample-gerkhin-webengine +> https://github.com/AxaFrance/webengine-java/sample-gerkhin-webengine + diff --git a/src/AxaFrance.WebEngine.Doc/tutorials/intro.md b/src/AxaFrance.WebEngine.Doc/tutorials/intro.md index 29a4879..01c504a 100644 --- a/src/AxaFrance.WebEngine.Doc/tutorials/intro.md +++ b/src/AxaFrance.WebEngine.Doc/tutorials/intro.md @@ -27,9 +27,9 @@ Understand basic concepts about |Test Approachs | .NET Sample | Java Sample| |---------------|-------------|------------| -Linear Scripting Approach | [Linear Scripting .NET](linear-script-cs.md) | Java -Structured Approach (Gherkin) | [Specflow](gherkin-cs.md) | Java -Keyword Driven Approach | [Keyword Driven](keyword-driven-cs.md) | Java -Data Driven Approach | [Data Driven](data-driven-cs.md) | Java +Linear Scripting Approach | [Linear Scripting .NET](linear-script-cs.md) | [Linear Scripting JAVA](linear-script-java.md) +Structured Approach (Gherkin) | [Specflow](gherkin-cs.md) | [JUnit](gherkin-java.md) +Keyword Driven Approach | [Keyword Driven](keyword-driven-cs.md) | [Keyword Driven](keyword-driven-java.md) +Data Driven Approach | [Data Driven](data-driven-cs.md) | [Data Driven](data-driven-java.md) diff --git a/src/AxaFrance.WebEngine.Doc/tutorials/keyword-driven-java.md b/src/AxaFrance.WebEngine.Doc/tutorials/keyword-driven-java.md index c955640..1914b14 100644 --- a/src/AxaFrance.WebEngine.Doc/tutorials/keyword-driven-java.md +++ b/src/AxaFrance.WebEngine.Doc/tutorials/keyword-driven-java.md @@ -1 +1,536 @@ -![Toc Workaround](toc-workaround.md)# Tutorials +# Keyword-Driven testing Approach (Java/Spring boot) +This article will show steps to build an Automation Solution based on Keyword-Driven testing approach with WebEngine Framework. +Please refer to [Keyword-Driven testing approach](keyworddriven.md) for more information about this approach. + +Considering we are building a Test Automation Solution for Home Insurance Underwriting application: http://webengine-test.azurewebsites.net/home-insurance + +## Step 1: Analyzing and Modelling +In this step, we analyze application under test, create use cases and high-level actions (keywords), note that these actions can be possibly reused in different test cases. + +For example, we can separate the insurance underwriting scenario into several high-level action keywords: +* Login +* Search Prospect +* Underwriting +* Choose offer and options +* Validate contract +* Logout + +For underwriting, there will be 3 steps: +* Home location +* Details based on type of home: **Apartment** or **House**. +* And antecedents if it happened + +The modelling of Home Insurance Underwriting application may look like following diagram: + +![Schemas](../images/kd-schemas.png) + +Along with the modelling, it is necessary to define also test data to be used for each action keyword. these Test datas are manipulated in the application under test and also verify expected result. + +## Step 2: Prerequisites for Keyword-Driven Approach + +JDK 8, maven and lombok + + +## Step 3: Create a simple maven project + +Open pom.xml paste below code + +```xml + + + +4.0.0 + +fr.axa.automation +sample-keyword-driven-webengine-java +1.0.0-SNAPSHOT +jar +sample-keyword-driven-webengine-java + + + UTF-8 + fr.axa.automation.Application + + 2.2.0.RELEASE + + 2.5.4 + 3.10.1 + 3.0.0-M7 + 2.2.0 + + + + + + fr.axa.automation.webengine + webengine-boot-keyword-driven + ${webengine-boot-keyword-driven.version} + + + + + + + + + org.springframework.boot + spring-boot-starter-parent + ${spring-boot-starter.version} + pom + import + + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven-compiler-plugin.version} + + + + org.springframework.boot + spring-boot-maven-plugin + ${spring-boot-maven-plugin.version} + + ${start-class} + exec + true + + + + + repackage + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + ${maven-surefire-plugin.version} + + false + + + + + org.apache.maven.plugins + maven-dependency-plugin + + + unpack + generate-resources + + unpack + + + + + fr.axa.automation.webengine + webengine-report + 3.0.0-SNAPSHOT + jar + html-report/** + ${basedir}/src/main/resources/ + + + + + + + + + + + + + +``` + +> [!NOTE] +> Run the command "mvn clean install -U" + +## Step 3: Initialize project structure +A typical WebEngine test project has following structures: + + + + + +
+ +![](../images/java/keyword-driven/keyword-driven-project-structure.png) + + + + +#### model +This folder contains all page models. +It is recommended to create one page model per web page (or screen). +If a web page has too many UI elements to manipulate during the test, they can also be stored in different page models. + +#### action +This folder contains all Action Keywords. + +#### teststep +Test step associated to action. + +#### testcase +Defines test cases by reusing action keywords. +A test case can be reused multiple times by giving different test data (combination with Data-driven approach) + +#### Testsuite +The entry point of the test project. It lists the test cases should be executed. + +
+ +## Step 4: Define page models +Similar to other methods, it is recommended to use Page model to separate object identification and test script. + +Please refer to [Organize UI Elements with Page Model](page-model.md) +* For Web application, use [WebElementDescription](web-elements.md) + +[//]: # (* For Native mobile app, use [AppElementDescription](app-elements.md)) + +In this example, we will define one model per page. +* Please refer to section [Implementing Login keyword](#implementing-login-keyword) + + +# [Login page model](#tab/login-page-model) +```java +package fr.axa.automation.model; + +import fr.axa.automation.webengine.core.AbstractPageModel; +import fr.axa.automation.webengine.core.WebElementDescription; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.experimental.FieldDefaults; +import org.openqa.selenium.WebDriver; + + +@FieldDefaults(level = AccessLevel.PUBLIC) +public class LoginModel extends AbstractPageModel { + + @Getter + WebElementDescription login = WebElementDescription.builder().tagName("input").name("login").build(); + + @Getter + WebElementDescription password = WebElementDescription.builder().tagName("input").name("password").build(); + + @Getter + WebElementDescription button = WebElementDescription.builder().tagName("button").innerText("Login").build(); + + public LoginModel(WebDriver webDriver) throws Exception { + populateDriver(webDriver); + } +} +``` + +# [search prospect page model](#tab/search-prospect-page-model) +```java +package fr.axa.automation.model; + +import fr.axa.automation.webengine.core.AbstractPageModel; +import fr.axa.automation.webengine.core.WebElementDescription; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.experimental.FieldDefaults; +import org.openqa.selenium.WebDriver; + + +@FieldDefaults(level = AccessLevel.PUBLIC) +public class ProspectModel extends AbstractPageModel { + + @Getter + WebElementDescription customerId = WebElementDescription.builder().tagName("input").id("prospectId").build(); + + @Getter + WebElementDescription customerName = WebElementDescription.builder().tagName("input").name("prospectName").build(); + + @Getter + WebElementDescription searchName = WebElementDescription.builder().xPath("/html/body/div/div[2]/button").build(); + + public ProspectModel(WebDriver webDriver) throws Exception { + populateDriver(webDriver); + } +} +``` +*** + +## Step 5: Develop test scripts +Now we are entering to the most interesting part: develop test scripts. +That is, implementing each action keyword with test script. In this article, we will show the implementation of 2 keywords: +`Login` and `searching module` + +`Login` is a normal keyword doing following actions: +* Connect to the application. +* Fill username in the textbox +* Fill password in the textbox +* Click on "Login" button +* Check the login is success + +This action needs 3 parameters: `URL` of the application, `Username` and `Password` + +`searching module` is a page for searching customer : +Enter the name or id of customer +Click on the "search" button + +> [!NOTE] +> In this tutorial, we will use hard coded test data for demonstration. +> +> Externalized test data and dynamic test suite will be discussed in [Data-Driven testing approach](data-driven-cs.md) + +### Implementing `Login` keyword + +# [Login page](#tab/login-page) + +![Login Web page](../images/kd-login-web.png) + +# [Login action](#tab/login-action) +```java +package fr.axa.automation.action; + +import fr.axa.automation.model.LoginModel; +import fr.axa.automation.webengine.core.AbstractActionWebBase; +import lombok.AccessLevel; +import lombok.experimental.FieldDefaults; + +@FieldDefaults(level = AccessLevel.PRIVATE) +public class LoginAction extends AbstractActionWebBase { + LoginModel loginModel; + + public LoginAction() { + } + + @Override + public void doAction() throws Exception { + loginModel = new LoginModel(getWebDriver()); + getWebDriver().get("http://webengine-test.azurewebsites.net/home-insurance/"); + loginModel.getLogin().sendKeys("test"); + loginModel.getPassword().sendKeys("test"); + screenShot(); + loginModel.getButton().click(); + addInformation("First step succeed"); + } + + @Override + public boolean doCheckpoint() throws Exception { + return true; + } +} +``` + +# [Login step](#tab/login-step) + +```java +package fr.axa.automation.teststep; + +import fr.axa.automation.action.LoginAction; +import fr.axa.automation.webengine.core.IAction; +import fr.axa.automation.webengine.core.ITestStep; + +public class LoginStep implements ITestStep { + @Override + public Class getAction() { + return LoginAction.class; + } +} +``` +*** + +### Implementing `searching module` keyword + +# [Searching module page](#tab/searching-module-page) + +![](../images/search-prospect.png) + +# [Searching module action](#tab/searching-module-action) + +```java +package fr.axa.automation.action; + +import fr.axa.automation.model.ProspectModel; +import fr.axa.automation.webengine.core.AbstractActionWebBase; +import lombok.AccessLevel; +import lombok.experimental.FieldDefaults; + +@FieldDefaults(level = AccessLevel.PRIVATE) +public class ProspectAction extends AbstractActionWebBase { + ProspectModel prospectModel; + + public ProspectAction() { + } + + @Override + public void doAction() throws Exception { + prospectModel = new ProspectModel(getWebDriver()); + prospectModel.getCustomerName().sendKeys("Nicolas"); + prospectModel.getSearchName().click(); + screenShot(); + addInformation("Prospect step succeed"); + } + + @Override + public boolean doCheckpoint() throws Exception { + return true; + } +} +``` + +# [Searching module step](#tab/searching-module-step) + +```java +package fr.axa.automation.teststep; + + +import fr.axa.automation.action.ProspectAction; +import fr.axa.automation.webengine.core.IAction; +import fr.axa.automation.webengine.core.ITestStep; + +public class ProspectStep implements ITestStep { + @Override + public Class getAction() { + return ProspectAction.class; + } +} +``` +*** + +## Step 6: Define test cases +Test case is inherited from "fr.axa.automation.webengine.core.ITestCase" to have common web testing behaviors such as: Checks WebDriver, Open the browser before the test and close the browser after test. +And defines which keywords will be executed one after another. As per modelling, the test case will seem to the following code snippet: + +```java +package fr.axa.automation.testcase; + +import fr.axa.automation.teststep.LoginStep; +import fr.axa.automation.teststep.ProspectStep; +import fr.axa.automation.webengine.core.ITestCase; +import fr.axa.automation.webengine.core.ITestStep; + +import java.util.Arrays; +import java.util.List; + +public class FindProspectTestCase implements ITestCase { + + @Override + public List getTestStepList() { + return Arrays.asList( new LoginStep(), + new ProspectStep()); + } +} +``` + +## Step 7: Define Test Suite +A test suite defines a list of test cases to be executed. +One Test Case can be used multiple times with different test data. +Only one `TestSuite` per project is allowed. + +In this example, We don't use externalized test data, all test data is hard coded in the keyword action. +In this case we will provide a hard-coded list of test cases with name: + +```java +package fr.axa.automation.testsuite; + +import fr.axa.automation.testcase.FindProspectTestCase; +import fr.axa.automation.webengine.core.AbstractTestSuite; +import fr.axa.automation.webengine.core.ITestCase; + +import java.util.AbstractMap; +import java.util.ArrayList; +import java.util.List; + +public class TestSuite extends AbstractTestSuite { + + public static final String FIND_PROSPECT_TEST_CASE = "FIND_PROSPECT"; //Test case defined in data.xml + + @Override + public List> getTestCaseList() { + List> testCaseList = new ArrayList(); + testCaseList.add(new AbstractMap.SimpleEntry(FIND_PROSPECT_TEST_CASE,new FindProspectTestCase())); + return testCaseList; + } +} +``` + +> [!NOTE] +> Using hard coded test data and hard coded test case list is only for demonstration. +> In the real project, it is strongly recommanded to use variables in each keyword and run tests dynamically driven by external data. +> Please refer to [Data-Driven Test Approach](data-driven-cs.md) after finished this tutoriel. + +## Step 8: Define application boot +This is the main class for running your solution. +You have just to copy and paste this below code. + +```java +package fr.axa.automation; + +import fr.axa.automation.webengine.boot.BootProject; +import fr.axa.automation.webengine.logger.LoggerService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.CommandLineRunner; +import org.springframework.boot.WebApplicationType; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; + +@SpringBootApplication +public class Application implements CommandLineRunner { + + final LoggerService loggerService; + + final BootProject bootProject; + + @Autowired + public Application(LoggerService loggerService, BootProject bootProject) { + this.loggerService = loggerService; + this.bootProject = bootProject; + } + + public static void main(String[] args) { + new SpringApplicationBuilder(Application.class).web(WebApplicationType.NONE).run(args); + } + + @Override + public void run(String... args) throws Exception { + loggerService.info("Temporary directory application: "+System.getProperty("java.io.tmpdir")); + bootProject.runFromProject(args); + } +} +``` + + +## Step 9: Execute the solution +Run the class Application + +Then you can set breakpoint in the code to debug step by step. + + +> [!NOTE] +> When test data is externalized, you can run test cases dynamically from Excel via WebEngine Addin for Excel. +> For more information about Excel Add-in, please refer to [Working with WebEngine Add-in for Excel](../articles/excel-addin.md) + +## Step 10: Visualize Reports +After execution, the log will be generated in indicated folder. You can open it with Edge to see details and even screenshots: + +![](../images/java/keyword-driven/result-keyword-driven-test.png) + +When running you can add `-showReport` to open HTML report after test execution. + + +## Resume +As you can see, with this method it takes longer to automatize the first test case. But in long term, you have a well structured, modelized testing project that makes easier to improve and to maintain. We recommand this approach for complex end-to-end scenarios. + +BUT, it's not finished. In this tutorial, everything is hard-coded: +* Url is hard coded, you may want to test under multiple test-environments +* Test data is hard coded, you may want to run the test case with other combination of test data +* Test suite is hard coded, you may want to execute one, more or all test cases according to the need. +* Logic is hard coded, you may want to change some logic of script based on the test data. + In next article [Data-Driven Approach](data-driven-cs.md), we'll convert all these hard coded things dynamically. + +> [!NOTE] +> The example project of this article can be found in sample-keyword-driven diff --git a/src/AxaFrance.WebEngine.Doc/tutorials/linear-script-java.md b/src/AxaFrance.WebEngine.Doc/tutorials/linear-script-java.md index c955640..2645066 100644 --- a/src/AxaFrance.WebEngine.Doc/tutorials/linear-script-java.md +++ b/src/AxaFrance.WebEngine.Doc/tutorials/linear-script-java.md @@ -1 +1,232 @@ -![Toc Workaround](toc-workaround.md)# Tutorials +# Linear Scripting Approach (Java) +In this article we will show you how to write test cases using linear scripting approach. +Linear scripting approach is the most used approach in Unit Test and for simple test scenarios. +In this approach, test cases are interpreted directly into test scripts, the method is fast for writing simple test cases but does not guarantee reuse of the functionality and part of test script across test projects. + +This tutorial is based on JUnit 5, if you are using TestNG, or another library, the solution will be similar. + +> [!NOTE] +> Linear scripting approach is easy and fast to implement. +> +> Linear scripting approach does not guarantee modeling and reuse. Please consider avoiding this approach for end-to-end tests, because it may be too complex to be evolved and maintained. + +## Required Framework components +* `webengine-web`: for Web Applications running on Desktop and Mobile Devices. + +Other components of WebEngine Framework is not used. + +## Steps to build Test Automation Solution using Linear Scripting + +### Step 1: Prerequisites for linear Approach + +JDK 8, maven and lombok + +### Step 2: Create a Test Project + +Create a simple maven project. + +Open pom.xml paste below code + +```xml + + + + 4.0.0 + + fr.axa.automation + sample-test-linear + 1.0.0-SNAPSHOT + jar + sample-test-linear + + + UTF-8 + + + + + fr.axa.automation.webengine + webengine-web + 2.2.0 + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.10.1 + + 8 + 8 + + + + + org.apache.maven.plugins + maven-surefire-plugin + 3.0.0-M7 + + false + + + + + org.jacoco + jacoco-maven-plugin + 0.8.6 + + + + + + + + +``` +Now we can build you project with the command "mvn clean install -U". + +Our system under test is the application: http://webengine-test.azurewebsites.net/Step1.html + +### Step 3: Write Setup and Teardown +In this step, we will write `Setup` and `Teardown` functions to initialize test environment before each test case and cleanup the test results after each test case. + +* Setup: Initializes the WebDriver object. +* Teardown: Closes the driver and the browser. + +To do this, insert following code to `SampleTest.java` +```java +public class SampleTest { + + private Optional optionalWebdriver; + + @BeforeEach // setup() + public void setup() throws Exception { + optionalWebdriver = BrowserFactory.getWebDriver("Windows", "Chrome", Arrays.asList("--remote-allow-origins=*")); + } + + @AfterEach + void tearDown() { + WebDriver driver = optionalWebdriver.get(); + driver.quit(); + } + +} + +``` + +### Step 4: Observe SUT and identify UI Elements + +Observing system under test from Developer tools provided with browser. Here in our tutorial, we will operate 3 elements, to benefits the advantages from the Framework, we will put these 3 elements into a `PageModel`. + +![Step 3 UI Elements](../images/ls-step3-uielements.png) + +When identifying an UI Element, we can combine multiple locators to make sure they can select only the element we want: + +* The dropdown list "Choose Language" can be identified by `TagName = "select"` and `id="language"`. +* The radio button group "Choose to buy" can be identified by `name="fav_language"` which matches all 3 radio buttons in the same group. +* The button "Next" can be identified by the `TagName="button"` and `onclick attribute="testSleep()"` + +The PageModel for this web page can be coded like following snippet: +[!code-csharp[Main](../../Samples.LinearScripting/MyPageModel.cs "Page Model")] + + +### Step 5: Write test script. +Now we are ready to write the automated test script for this web page: +* Initialize the page model object +* Use the page model to interact with UI elements + +Instead of using native selenium commands, it is recommended to use actions implemented in `WebElementDescription`. The script will be easier to read, to understand and to maintain, because +most of actions are protected for web page changes such as page reload or asynchronized JavaScript. Without the pattern, script may encounter `NoSuchElementException` and `StaleElementReferenceException`. + +Using PageModel, you can fill the function TestMethod1 with following code snippet + +```java +package fr.axa.automation.feature.model; + +import fr.axa.automation.webengine.core.AbstractPageModel; +import fr.axa.automation.webengine.core.WebElementDescription; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.experimental.FieldDefaults; +import org.openqa.selenium.WebDriver; + + +@FieldDefaults(level = AccessLevel.PUBLIC) +public class FirstPageModel extends AbstractPageModel { + + @Getter + WebElementDescription language = WebElementDescription.builder().tagName("select").id("language").build(); + + @Getter + WebElementDescription coffeeRadio = WebElementDescription.builder().tagName("input").id("coffee").build(); + + @Getter + WebElementDescription teaRadio = WebElementDescription.builder().tagName("input").id("tea").build(); + + @Getter + WebElementDescription waterRadio = WebElementDescription.builder().tagName("input").id("water").build(); + + @Getter + WebElementDescription nextStep = WebElementDescription.builder().tagName("button").xPath(".//button[contains(text(),\"Next (3-second-delay)\")]").build(); + + public FirstPageModel(WebDriver webDriver) throws Exception { + populateDriver(webDriver); + } +} +``` + + +```java +public class SampleTest { + + private Optional optionalWebdriver; + + @BeforeEach + public void setup() throws Exception { + optionalWebdriver = BrowserFactory.getWebDriver("Windows", "Chrome", Arrays.asList("--remote-allow-origins=*")); + } + + @AfterEach + void tearDown() { + WebDriver driver = optionalWebdriver.get(); + driver.quit(); + } + + @Test + public void linearApproachWithPageModel() throws Exception { + String baseUrl = "https://axafrance.github.io/webengine-dotnet/demo/Step1.html"; + if(optionalWebdriver.isPresent()){ + WebDriver driver = optionalWebdriver.get(); + driver.get(baseUrl); + Page page = new Page(driver); + page.getLanguage().selectByText("Français"); + page.getCoffeeRadio().click(); + page.getNextStep().click(); + Assertions.assertTrue(page.getPageStep2().exists()); + } + } +} + +``` + +### Step 6: Run tests +To run the test case, click to the play button: +![](../images/java/linear/run-linear-test.png) + +Below the result +![Run Test](../images/java/linear/linear-result.png) + + +### Step 7: Improve the test cases +Now you can continue automatizing this scenario to the end by: +* Adding new UI elements into `PageModel` +* Manipulate these UI elements +* Retrieve information from these UI elements in order to compare with expected value + +> [!NOTE] +> The source code of this exercise can be found in our Github repository. +> Project Name: sample-test-linear +> https://github.com/AxaFrance/webengine-java/sample-test-linear +