Background:
Recently I have finished a project for which I had to do configure the generic web test automation framework in a way that is also suitable for mobile web testing without writing or modifying a lot of code structures. There are actually some benefits having one framework instead of two:
- Consistency and Reusability:
-
- Unified Test Scripts: We can write test scripts once and reuse them across both platforms, reducing redundancy and saving time.
- Uniform Reporting: With a single framework, the reporting format and test results are consistent, making it easier to track and compare results across web and mobile environments.
- Reduced Learning Curve:
-
- Single Skill Set: Testers need to learn and master only one framework, reducing the time and effort required for training.
- Simplified Maintenance: Maintenance of test scripts and framework configuration is easier with a unified approach, as changes need to be implemented only once.
- Cost Efficiency:
-
- Lower Licensing Costs: Using one framework often means fewer licenses to purchase and manage.
- Reduced Development Time: Developing and maintaining one set of test scripts and framework configurations is more cost-effective than handling multiple sets.
- Enhanced Collaboration:
-
- Improved Team Coordination: A common framework facilitates better collaboration among team members, as they are all familiar with the same tools and practices.
- Easier Integration: Integrating with CI/CD pipelines, version control, and other tools becomes more streamlined when using a single framework.
- Streamlined Test Management:
-
- Centralized Test Management: Managing tests, tracking issues, and analyzing results are more straightforward with a centralized system.
- Consistent Test Environments: Ensuring that test environments are consistent across web and mobile platforms becomes easier, leading to more reliable test results.
- Enhanced Flexibility and Scalability:
-
- Adaptability: A single framework can often be extended and adapted more easily to meet the evolving needs of both web and mobile testing.
- Scalable Solutions: It is simpler to scale testing efforts when the framework supports both platforms seamlessly.
- Improved Quality Assurance:
-
- Cross-Platform Testing: Ensuring that functionality works uniformly across web and mobile platforms improves the overall quality of the application.
- Comprehensive Coverage: A unified framework can provide better test coverage, ensuring that all critical aspects of the application are tested.
Frameworks like Selenium, Serenity (for web) and Appium (for mobile) exemplify this unified approach, as they allow for similar scripting and offer integration possibilities that make cross-platform testing more efficient and effective.
How to achieve this behavior:
Our project was built with Serenity, Selenium and Junit considering Page Object Methodologies.
First of all, we need to add Appium maven dependencies to the pom.xml file:
<dependency>
<groupId>io.appium</groupId>
<artifactId>java-client</artifactId>
<version>8.3.0</version>
<scope>test</scope>
</dependency>
and then we need to create two profiles, one for Web testing and the other one for native mobile web testing using Android Emulator under pom.xml
Profile for web testing:
<profile>
<id>web</id>
<build>
<directory>src/test/target/target-web</directory>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.1.2</version>
<configuration>
<parallel>classes</parallel>
<threadCount>10</threadCount>
</configuration>
</plugin>
<plugin>
<groupId>net.serenity-bdd.maven.plugins</groupId>
<artifactId>serenity-maven-plugin</artifactId>
<version>${serenity.version}</version>
<executions>
<execution>
<phase>post-integration-test</phase>
<goals>
<goal>aggregate</goal>
</goals>
<configuration>
<systemPropertyVariables>
<serenity.conf.file>src/test/resources/serenity-web.conf</serenity.conf.file>
</systemPropertyVariables>
<outputDirectory>src/test/reports/report-web/web-reports-${buildNumber}</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
The serenity-web.conf looks like:
serenity {
project.name = „Web Portal Test – Project Name“
take.screenshots = FOR_FAILURES
keep.unscaled.screenshots = true
screenshooter = shutterbug1x
console.colors = true
tag.failures = „true“
linked.tags = „issue“
}
shutterbug {
capturestrategy = FULL
}
headless.mode = false
TEST_ENVIRONMENT = „local“
webdriver {
driver = „chrome“
chrome.driver = „C:\\Users\\Islam\\IdeaProjects\\project_name\\driver\\chromedriver.exe“
capabilities {
browserName = „chrome“
platformName =“windows“
acceptInsecureCerts = true
„goog:chromeOptions“{
args = [
„remote-allow-origins=*“,
„start-maximized“,
„incognito“,
„no-sandbox“,
„disable-gpu“,
„disable-dev-shm-usage“,
„disable-popup-blocking“,
„disable-default-apps“,
„disable-extensions“,
„disable-infobars“,
„disable-web-security“,
„disable-translate“,
„disable-logging“,
„test-type“,
„ignore-certificate-errors“
],
prefs {
download.default_directory = ${user.dir}“\\downloaded-files“
download.prompt_for_download = false
credentials_enable_service = false
password_manager_enabled = false
}
}
proxy {
httpProxy = „user:password@http:port“
}
}
}
serenity.proxy.http = „Your_proxy_http“
serenity.proxy.http_port = „8080“
serenity.proxy.user = „Your_proxy_user“
serenity.proxy.password = “ Your_proxy_pass“
Profile for mobile web testing using Android emulator (emulator-5554):
<profile>
<id>emulator-5554</id>
<build>
<directory>src/test/target/target-5554</directory>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<executions>
<execution>
<configuration>
<parallel>classes</parallel>
<threadCount>1</threadCount>
<argLine>-Dudid=emulator-5554</argLine>
<argLine>-Dhub=http://localhost:4723/wd/hub</argLine>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>net.serenity-bdd.maven.plugins</groupId>
<artifactId>serenity-maven-plugin</artifactId>
<version>${serenity.version}</version>
<executions>
<execution>
<phase>post-integration-test</phase>
<goals>
<goal>aggregate</goal>
</goals>
<configuration>
<systemPropertyVariables>
<serenity.conf.file>src/test/resources/serenity-mobile.conf</serenity.conf.file>
</systemPropertyVariables>
<outputDirectory>src/test/reports/report-5554/emulator-5554-reports-${buildNumber}</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
The serenity-mobile.conf looks like this:
webdriver
{
driver = „appium“
}
appium {
#hub = „http://localhost:4723/wd/hub“
hub = „${hub}“ #For normal andriod studio emulator and appium desktop server, this line should be active # Retrieve udid from system property
#hub = „http://localhost:4723“ #For docker localhost:6080 emulator, this line should be active
automationName = „uiautomator2“
platformName = „Android“
platformVersion = „11.0“
udid = „${udid}“ # Retrieve udid from system property
newCommandTimeout = „1000“
browserName = „chrome“
#chromedriverExecutableDir = „/usr/appiumdriver/“ # Retrieve from run command
}
TEST_ENVIRONMENT = „mobile“
serenity {
project.name = “Mobile Portal Test – Project Name”
full.page.screenshot.strategy = true
screenshooter = shutterbug1x
take.screenshots = FOR_FAILURES
keep.unscaled.screenshots = true
console.colors = true
}
dashboard {
excluded.tag.list = version
}
shutterbug {
capturestrategy = FULL
}
headless.mode = true
Hopefully everyone will understand the .conf’s and how things are written between <profile> and </profile>, therefore, I am not going to provide further explanation on this topic. The main idea is to separate these elements to handle the execution and configuration individually.
‘Test classes’, ‘Steps classes’ and ‘Element repository’ stay as it is for both web testing and mobile web testing. All I need to do now is to create two different test suites.
………. TestSuiteMobileWeb.Java …………..
package com.myPackage.www;
import net.serenitybdd.junit5.SerenityJUnit5Extension;
import net.serenitybdd.core.Serenity;
import org.junit.jupiter.api.*;
import org.junit.jupiter.api.extension.ExtendWith;
import org.openqa.selenium.WebDriver;
import myPackage.actions.Interaction;
import myPackage.tests.*;
@ExtendWith(SerenityJUnit5Extension.class)
@DisplayName(„Mobile Web Test“)
public class TestSuiteMobileWeb {
private WebDriver driver;
private Interaction interaction;
private ContributionSubsidyTest contributionSubsidyTest;
@BeforeEach
void setUp() {
driver = Serenity.getDriver();
interaction = new Interaction();
contributionSubsidyTest = new ContributionSubsidyTest();
Serenity.injectScenarioStepsInto(contributionSubsidyTest);
}
@BeforeAll
static void setupEnvironment() {
System.setProperty(„test.type“, „mobile“);
}
@Test
@DisplayName(„Application for the contribution subsidy – Applying for a contribution subsidy as a single person“)
void contributionSubsidySinglePersonTest () {
contributionSubsidyTest.contributionSubsidySinglePersonTest(driver);
driver.quit();
}
}
……… TestSuiteWeb.Java ……….
package com.myPackage.www;
import net.serenitybdd.junit5.SerenityJUnit5Extension;
import net.serenitybdd.core.Serenity;
import org.junit.jupiter.api.*;
import org.junit.jupiter.api.extension.ExtendWith;
import org.openqa.selenium.WebDriver;
import myPackage.actions.Interaction;
import myPackage.tests.*;
@ExtendWith(SerenityJUnit5Extension.class)
@DisplayName(„Web Test“)
public class TestSuiteWeb {
private WebDriver driver;
private Interaction interaction;
private ContributionSubsidyTest contributionSubsidyTest;
@BeforeEach
void setUp() {
driver = Serenity.getDriver();
interaction = new Interaction();
contributionSubsidyTest = new ContributionSubsidyTest();
Serenity.injectScenarioStepsInto(contributionSubsidyTest);
}
@BeforeAll
static void setupEnvironment() {
System.setProperty(„test.type“, „web“);
}
@Test
@DisplayName(„Application for the contribution subsidy – Applying for a contribution subsidy as a single person“)
void contributionSubsidySinglePersonTest () {
contributionSubsidyTest.contributionSubsidySinglePersonTest(driver);
driver.quit();
}
}
Method: setupEnvironment
static void setupEnvironment() {
System.setProperty(„test.type“, „mobile“);
}
- Purpose: The purpose of this method is to configure the environment for the tests by setting a system property.
- System Property:
- System.setProperty(„test.type“, „mobile“): This line sets a system property named test.type with the value mobile.
- System Properties: System properties are key-value pairs used to configure the runtime behavior of the application or test environment. They can be accessed throughout the Java application using System.getProperty(key).
Usage Context
- Mobile Testing Configuration: By setting the test.type property to mobile, the method configures the test environment specifically for mobile testing. This could influence various parts of the test suite or application under test, such as which configurations or test data to use, or which components to initialize.
- Environment-Specific Behavior: Other parts of the codebase can check the value of the test.type property to alter their behavior accordingly. For example:
if („mobile“.equals(System.getProperty(„test.type“))) {
// Execute mobile-specific setup or tests
}
In my case, the old contributionSubsidySinglePersonTest method was built for generic web testing only and for mobile testing, now it needs to be corrected like this:
Before:
@Test
@DisplayName(„Applying for a contribution subsidy as a single person“)
public void contributionSubsidySinglePersonTest (WebDriver driver) {
interaction.openThePortalLoginPage();
interaction.loginWithCredentials(testuser.STEVE, testpass.USER_PW_ADMIN);
interaction.click(dashboardWebElements.TILE_PENSION_FUND);
interaction.click(dashboardWebElements.TILE_GRANT_APPLICATION);
contributionSubsidySteps.applicationAsSinglePerson (driver);
}
}
After:
@Test
@DisplayName(„Applying for a contribution subsidy as a single person“)
public void contributionSubsidySinglePersonTest (WebDriver driver) {
String testType = System.getProperty(„test.type“,““);
if („mobile“.equals(testType)){
interaction.openThePortalLoginPageFromMobile(driver);
interaction.loginWithCredentials(testuser.STEVE, testpass.USER_PW_ADMIN);
interaction.click(dashboardWebElements.TILE_PENSION_FUND);
interaction.click(dashboardWebElements.TILE_GRANT_APPLICATION);
contributionSubsidySteps.applicationAsSinglePerson (driver);
} else if („web“.equals(testType)){
interaction.openThePortalLoginPage();
interaction.loginWithCredentials(testuser.STEVE, testpass.USER_PW_ADMIN);
interaction.click(dashboardWebElements.TILE_PENSION_FUND);
interaction.click(dashboardWebElements.TILE_GRANT_APPLICATION);
contributionSubsidySteps.applicationAsSinglePerson (driver);
}
}
For all other tests, the same procedure needs to be adapted.
So, now we need to decide which test suite we have to run. That’s it. The system will automatically understand whether this is a generic web test or a web test on a mobile device (emulator).
Run command for Android emulator (Mobile Web Test):
mvn clean verify -P emulator-5554 „-Dappium.udid=emulator-5554“ „-Dappium.hub=http://localhost:4723/wd/hub“ „-Dwebdriver.driver=appium“ „-Dappium.automationName=uiautomator2“ „-Dappium.platformName=Android“ „-Dappium.platformVersion=11.0“ „-Dappium.newCommandTimeout=1000“ „-Dappium.browserName=chrome“ „-Dserenity.conf.file=src/test/resources/serenity-mobile.conf“ „-DTEST_ENVIRONMENT=mobile“ „-Dtest=myPackage.TestSuiteMobileWeb.java“
If no appium driver found, then we need to see which chrome driver version the emulator has (normally 83.0.4103.106). Then we need to download the chromedriver locally and run the code like:
mvn clean verify -P emulator-5554 „-Dappium.udid=emulator-5554“ „-Dappium.hub=http://localhost:4723/wd/hub“ „-Dwebdriver.driver=appium“ „-Dappium.automationName=uiautomator2“ „-Dappium.platformName=Android“ „-Dappium.platformVersion=11.0“ „-Dappium.newCommandTimeout=1000“ „-Dappium.browserName=chrome“ „-Dserenity.conf.file=src/test/resources/serenity-mobile.conf“ „-DTEST_ENVIRONMENT=mobile“ „-Dtest=myPackage.TestSuiteMobileWeb.java“ „-Dappium.chromedriverExecutable=C:\path of project\src\test\driver\chromedriver.exe“
Run command for generic web test:
mvn clean verify -Pweb „-Dserenity.conf.file=src/test/resources/serenity-web.conf“ „-Dudid=web“ „-DTEST_ENVIRONMENT=local“ „-Dwebdriver.chrome.driver=C:\\Users\\Md Toufiqul Islam\\IdeaProjects\\portal-serenity-tests\\src\\test\\driver\\chromedriver.exe“ „-Dtest=myPackage.TestSuiteWeb“
Report:
After successful or unsuccessful test execution, a report will be generated (with screenshots) based on the parameter given in pom.xml file for each profile and also based on serenity-mobile.conf / serenity-web.conf file settings.