Build an E2E test environment with Selenium (Java)

~~18/11/29 If you update Selenide to the latest version (ver5 series), it will not work. It is better to use the latest version, so I will fix it later. ~~ 18/12/03 I tried only Chrome and updated the program code. IE etc. remains as it is in the description of capabilities.json.

Introduction

Instant QA: innocent: In order to gradually improve the current situation where regression testing is taking time, we built an environment to automate system testing that is doing the same thing repeatedly. I have that record.

I haven't recorded how to run the test because it is better to refer to Pitalium Tutorial.

Software used

This is the software used for verification. Everything was great. You can test with Selenium Grid (Hub, Node configuration) using Pitalium. I am building a verification build on Windows.

soft version Use
java 1.8.0_191
Pitalium 1.2.4 Used as a base
Selenide 5.0.1 Used only with PageObject
selenium-server-standalone.jar 3.141.59
chromedriver.exe 2.44

Environment

Proceed with Pitalium Tutorial to build a test execution environment.

18/04/23 ClassNotFound may occur in CI (Jenkins), so I changed it to Maven. The scope is not test because I'm trying it in a test project.

Maven file
Maven file (decompression)

pom.xml


<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>Autotest</groupId>
  <artifactId>Autotest</artifactId>
  <version>0.0.1</version>
  <build>
    <sourceDirectory>src</sourceDirectory>
    <testSourceDirectory>src</testSourceDirectory>
    <resources>
      <resource>
        <directory>resource</directory>
      </resource>
    </resources>
    <testResources>
      <testResource>
        <directory>resource</directory>
      </testResource>
    </testResources>
    <plugins>
      <plugin>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.3</version>
      </plugin>
      <plugin>
        <artifactId>maven-surefire-plugin</artifactId>
        <version>2.12.4</version>
      </plugin>
    </plugins>
  </build>
  <dependencies>
  	<dependency>
  		<groupId>com.htmlhifive</groupId>
  		<artifactId>pitalium</artifactId>
  		<version>1.2.4</version>
  		<exclusions>
  			<exclusion>
  				<groupId>org.slf4j</groupId>
  				<artifactId>slf4j-api</artifactId>
  			</exclusion>
  		</exclusions>
  	</dependency>
  	<dependency>
  		<groupId>com.codeborne</groupId>
  		<artifactId>selenide</artifactId>
  		<version>5.0.1</version>
  		<exclusions>
  			<exclusion>
  				<groupId>org.apache.httpcomponents</groupId>
  				<artifactId>httpclient</artifactId>
  			</exclusion>
  			<exclusion>
  				<groupId>org.slf4j</groupId>
  				<artifactId>slf4j-api</artifactId>
  			</exclusion>
  		</exclusions>
  	</dependency>
  	<dependency>
  		<groupId>org.seleniumhq.selenium</groupId>
  		<artifactId>selenium-api</artifactId>
  		<version>3.141.59</version>
  	</dependency>
  	<dependency>
  		<groupId>org.seleniumhq.selenium</groupId>
  		<artifactId>selenium-remote-driver</artifactId>
  		<version>3.141.59</version>
  	</dependency>
  	<dependency>
  		<groupId>org.seleniumhq.selenium</groupId>
  		<artifactId>selenium-support</artifactId>
  		<version>3.141.59</version>
  	</dependency>
  	<dependency>
  		<groupId>com.google.guava</groupId>
  		<artifactId>guava</artifactId>
  		<version>27.0.1-jre</version>
  	</dependency>
  	<dependency>
  		<groupId>org.slf4j</groupId>
  		<artifactId>slf4j-log4j12</artifactId>
  		<version>1.8.0-beta2</version>
  	</dependency>
  	<dependency>
  		<groupId>org.apache.httpcomponents</groupId>
  		<artifactId>httpclient</artifactId>
  		<version>4.5.6</version>
  	</dependency>
  </dependencies>
  <properties>
    <java.version>1.8</java.version>
    <file.encoding>UTF-8</file.encoding>
  	<project.build.sourceEncoding>${file.encoding}</project.build.sourceEncoding>
  	<project.reporting.outputEncoding>${file.encoding}</project.reporting.outputEncoding>
    <maven.compiler.encoding>${file.encoding}</maven.compiler.encoding>
    <maven.compiler.source>${java.version}</maven.compiler.source>
    <maven.compiler.target>${java.version}</maven.compiler.target>
  </properties>
</project>

My environment looks like this: Due to the problem described later, IE was finally launched separately on another node.

Hub start command
Hub startup command (expand)
java -jar selenium-server-standalone-3.141.59.jar -role hub
Node config file
Node configuration file (decompression)

NodeConfigBrowser.json


{
 "capabilities": [
    {
     "platform": "WINDOWS",
     "browserName": "firefox",
     "maxInstances": 2,
     "seleniumProtocol": "WebDriver"
    },
    {
     "platform": "WINDOWS",
     "browserName": "chrome",
     "maxInstances": 3,
     "seleniumProtocol": "WebDriver"
    },
    {
     "platform": "WINDOWS",
     "browserName": "internet explorer",
     "maxInstances": 1,
     "seleniumProtocol": "WebDriver"
    }
  ],
 "hub": "http://XXX.XXX.XXX.XXX:4444/grid/register",
 "register": true
}
Node start command
Node start command (expand)
java -Dwebdriver.ie.driver=IEDriverServer.exe -Dwebdriver.chrome.driver=chromedriver.exe -Dwebdriver.gecko.driver=geckodriver.exe -jar selenium-server-standalone-3.141.59.jar -role node -nodeConfig NodeConfigBrowser.json

Program structure

The directory structure may change, but for the time being, I made it with the following structure.

aWS050227.JPG

--page package

Attracted by the sound of the PageObject pattern, I decided to create one screen with one object for the time being. I'm using Selenide here.

~~ Since the generation of WebDriver is left to Pitalium, I created a parent class and passed WebDriver to Selenide in the constructor. ~~ 18/12/03 The latest version of Selenide (ver5 series) had a problem here. Changed to specify the driver to be used by Selenide only once in the test case parent class.

Parent Page class
Parent Page class (expansion)

PageBase.java


package page;

import com.htmlhifive.pitalium.core.config.PtlTestConfig;

/**
 *Page parent class for common processing such as header and footer in the page
 */
public abstract class PageBase {

    /**Base URL starting with http or https*/
    protected final String _BASE_URL = PtlTestConfig.getInstance().getTestAppConfig().getBaseUrl();

    /*********************************
     *Common operation
     *********************************/

}
Child Page class
Child Page class (expansion)

The reason why the class name etc. is in Japanese will be described later.

Login.java


package page;

import static com.codeborne.selenide.Selenide.*;

public class __Login screen extends PageBase{

    public static final String _URL = "/login";

    /*********************************
     *selector
     *********************************/

    //Account ID
    private static final String _ACCOUNT_TXT_CSS = "#accountID";
    //password
    private static final String _PASS_TXT_CSS = "#password";
    //Login button
    private static final String _LOGIN_BTN_CSS = "#login";

    /*********************************
     *operation
     *********************************/

    public __Login screen__Enter your account ID(String s) {
        $(_ACCOUNT_TXT_CSS).setValue(s);
        return this;
    }

    public __Login screen__Enter password(String s) {
        $(_PASS_TXT_CSS).setValue(s);
        return this;
    }

    public __Login screen__Click the login button() {
        $(_LOGIN_BTN_CSS).click();
        return this;
    }

}

--test package

Created by inheriting the Pitalium class. We are doing test operations and image comparisons.

Again, I decided to put in a parent class as a base for the time being.

Parent test class
Parent test class (expansion)

TestBase.java


package test;

import java.util.Locale;

import org.junit.Before;
import org.junit.Rule;

import com.codeborne.selenide.Configuration;
import com.codeborne.selenide.WebDriverRunner;
import com.codeborne.selenide.commands.Commands;
import com.codeborne.selenide.junit.TextReport;
import com.google.common.base.Strings;
import com.htmlhifive.pitalium.core.PtlTestBase;
import com.htmlhifive.pitalium.core.model.ScreenshotArgument;

import custom.SetValueCustomize;

public abstract class TestBase extends PtlTestBase {

    /**Selenide Performance Report*/
    @Rule
    public TextReport report = new TextReport();

    private int _ssCount = 1;

    @Override
    @Before
    public void setUp() {
        super.setUp();

        //Replaced with the command that added the processing for IE
        Commands.getInstance().add("setValue", new SetValueCustomize(Strings.nullToEmpty(capabilities.getBrowserName()).toLowerCase(Locale.ENGLISH)));

        //Set element search timeout to 10 seconds and set to Selenide driver
        Configuration.timeout = 10000;
        WebDriverRunner.setWebDriver(driver);

        //The default is no performance report output
        report.onSucceededTest(false);
        report.onFailedTest(false);

        //Test execution pre-processing
        init();
    }

    /**
     *Test execution pre-processing
     */
    protected void init() {};

    /**
     *Enable Selenide Performance Reporting
     */
    protected void enablePerfReport() {
        report.onSucceededTest(true);
        report.onFailedTest(true);
    }

    /**
     *Determine if it is a Chrome browser
     * @return true:Chrome
     */
    protected boolean isChrome(){
        return "chrome".equals(Strings.nullToEmpty(capabilities.getBrowserName()).toLowerCase(Locale.ENGLISH)) ? true : false;
    }

    /**
     *Determine if it is an Edge browser
     * @return Edge
     */
    protected boolean isEdge(){
        return "MicrosoftEdge".equals(Strings.nullToEmpty(capabilities.getBrowserName()).toLowerCase(Locale.ENGLISH)) ? true : false;
    }

    /**
     *Determine if it is an IE browser
     * @return IE
     */
    protected boolean isIE(){
        return "internet explorer".equals(Strings.nullToEmpty(capabilities.getBrowserName()).toLowerCase(Locale.ENGLISH)) ? true : false;
    }

    /**
     *Take a screenshot
     */
    protected void screenShotAndVerifyView() {
        screenShotAndVerifyView("image");
    }

    /**
     *Take a screenshot
     * @param screenshot name
     */
    protected void screenShotAndVerifyView(String screenName) {
        ScreenshotArgument arg = ScreenshotArgument.builder(screenName + _ssCount).addNewTarget().build();
        assertionView.verifyView(arg);
        _ssCount++;
    }

}
Child test class
Child test class (expansion)

LoginTest.java


package test;

import org.junit.Test;

import page.__Login screen;

public class LoginTest extends TestBase {
    @Test
    public void loginOK() throws Exception {
        driver.get(__Login screen._url);
        __Login screen page= new __Login screen();

        page.__Enter your account ID("user")
                .__Enter password("password")
                .__Click the login button();

        assertionView.assertView("loginOK");
    }
}

Be careful

Since some test teams are reluctant just because they are horizontal characters, we decided to write all the classes and methods related to screen operations in Japanese.

In addition, the screen operation method is also designed so that candidates can be easily narrowed down with two underscores.

I think that the threshold is high because there is no one who has coding experience in quality, so I would like to proceed with the page class in development.

page class: Development code writing test class: Quality & test team coded

trouble

I don't think I've had much trouble with the operation I want to do: hugging: However, IE and Teme are not good.

Text input doesn't work in IE

I don't know the reason, but the phenomenon that input fails only in IE occurs. In IE, there are various information such as it is better to put controls in advance and clicks are unclear, so I did not understand so I decided to do everything: rolling_eyes: Corresponds to the process of inputting with Selenide by hooking (to be exact, it may be replaced).

Custom command class
Custom command class (expansion)

SetValueCustomize.java


package common;

import java.io.IOException;

import org.openqa.selenium.Keys;
import org.openqa.selenium.NoSuchElementException;
import org.openqa.selenium.WebElement;

import com.codeborne.selenide.Configuration;
import com.codeborne.selenide.SelenideElement;
import com.codeborne.selenide.commands.Commands;
import com.codeborne.selenide.commands.SetValue;
import com.codeborne.selenide.impl.WebElementSource;

/**
 *SetValue replacement class
 *The difference is IE individual correspondence
 */
public class SetValueCustomize extends SetValue {

    private boolean _ieFlg;

    public SetValueCustomize(String _browserName) {
        _ieFlg = "internet explorer".equals(_browserName) ? true : false;
    }

    @Override
    public WebElement execute(SelenideElement proxy, WebElementSource locator, Object[] args) {
        WebElement element = locator.findAndAssertElementIsVisible();

        if (Configuration.versatileSetValue
                && "select".equalsIgnoreCase(element.getTagName())) {
            return super.execute(proxy, locator, args);
        }
        if (Configuration.versatileSetValue
                && "input".equalsIgnoreCase(element.getTagName()) && "radio".equals(element.getAttribute("type"))) {
            return super.execute(proxy, locator, args);
        }

        if (_ieFlg) {
            element.sendKeys(Keys.CONTROL);
            try {
                Commands.getInstance().execute(proxy, locator, "click", null);
            } catch (IOException e) {
                throw new NoSuchElementException("IE Click error in SetValueCustomize", e);
            }
        }

        return super.execute(proxy, locator, args);
    }
}

Replace the created command in advance with the parent test class.

TestBase.java



public abstract class TestBase extends PtlTestBase {
    @Override
    @Before
    public void setUp() {
        super.setUp();
        //Replaced with the command that added the processing for IE
        Commands.getInstance().add("setValue", new SetValueCustomize(Strings.nullToEmpty(capabilities.getBrowserName()).toLowerCase(Locale.ENGLISH)));
    }
}

When the setValue method is called in various page classes, the class associated with the specified method name is acquired from the Map of Commands inside Selenide, and execute is called.

Login.java



public class login extends PageBase{

public login__Enter your login ID(String id) {
        $("#account").setValue(id);
        return this;
    }
}

In IE, the Hover menu is displayed for only 0.1 seconds (experience) and child elements cannot be selected.

Leave the mouse as it is! It seemed that it wouldn't be solved even if I screamed, so I dealt with it by specifying the capabilities with se: ieOptions.

capabilities ieOptions additional version
capabilities.json

capabilities.json


[
 {
  "browserName": "internet explorer",
  "se:ieOptions" : {
    "enablePersistentHover" : true,
    "requireWindowFocus" : true
  }
 }
]

Due to these things, it was decided arbitrarily to prepare a Node (terminal) that is dedicated to IE, IE starts only one process, and does not allow the operation or remote of others: innocent:

NodeConfig for IE-only terminal
NodeConfig for IE-only terminal

NodeConfigBrowser.json


{
 "capabilities": [
    {
     "platform": "WINDOWS",
     "browserName": "internet explorer",
     "maxInstances": 1,
     "seleniumProtocol": "WebDriver"
    }
  ],
 "hub": "http://XXXXX:4444/grid/register",
 "register": true
}

In IE and Firefox, a dialog etc. is displayed when downloading and the operation does not work (unsolved * It may be solved by using WinAppDriver. *)

Firefox tried setting capabilities with moz: firefoxOptions --prefs, but it didn't work, so I decided not to download test other than Chrome: innocent:

capabilities firefoxOptions additional version (meaningless)
(meaningless) capabilities.json

capabilities.json


[
 {
  "browserName": "firefox",
  "moz:firefoxOptions": {
    "prefs": {
        "browser.download.folderList": 0,
        "browser.download.useDownloadDir": true,
        "browser.helperApps.neverAsk.saveToDisk": "text/*,application/*",
        "browser.helperApps.alwaysAsk.force": false,
        "browser.download.manager.closeWhenDone": true,
        "pdfjs.enabledCache.state": false
    }
  }
 }
]

Recommended Posts

Build an E2E test environment with Selenium (Java)
Create an E2E test environment with Docker x Cypress
Java EE test (CDI / interceptor) with Arquillian
Build an environment with Docker on AWS
Build a Windows application test environment with Selenium Grid, Appium, and Windows Application Driver
Build and test Java + Gradle applications with Wercker
[Rails] How to build an environment with Docker
Build Java with Wercker
[First team development ②] Build an environment with Docker
Build a Java development environment with VS Code
Build Java development environment with VS Code on Mac
Build an environment of Ruby2.7.x + Rails6.0.x + MySQL8.0.x with Docker
Build Java development environment with WSL2 Docker VS Code
How to build Java development environment with VS Code
[Environment construction] Build a Java development environment with VS Code!
Build Java program development environment with Visual Studio Code
Enable Java EE with NetBeans 9
Build docker environment with WSL
What happened in "Java 8 to Java 11" and how to build an environment
Build WebAPP development environment with Java + Spring with Visual Studio Code
Create an immutable class with JAVA
Build a Java project with Gradle
Execution environment test after Java installation
Build a Node.js environment with Docker
Build a Tomcat 8.5 environment with Pleiades 4.8
[Java EE] Implement Client with WebSocket
Prepare Java development environment with Atom
Test with RSpec + Capybara + selenium + chromedriver
Build PlantUML environment with VSCode + Docker
Build environment with vue.js + rails + docker
Build Rails environment with Docker Compose
Java build with mac vs code
Build Java development environment (for Mac)
Follow the link with Selenium (Java)
Build jooby development environment with Eclipse
[Java] Test private methods with JUnit
Run an application made with Java8 with Java6
Build docker + laravel environment with laradock
Build an environment where pip3 can be used with CentOS7 + Python3
I tried to build an http2 development environment with Eclipse + Tomcat
Prepare Java development environment with VS Code
[Java] Create an executable module with Gradle
Build a PureScript development environment with Docker
Build an environment of "API development + API verification using Swagger UI" with Docker
Build and manage RStudio environment with Docker-compose
One-JAR Java EE application with WebSphere Liberty
CICS-Run Java applications-(3) Build management with Gradle
01. I tried to build an environment with SpringBoot + IntelliJ + MySQL (MyBatis) (Windows10)
Build a Java development environment on Mac
Build Java 8 development environment on AWS Cloud9
Build a Wordpress development environment with Docker
CICS-Run Java applications-(2) Build management with Maven
Read xlsx file in Java with Selenium
Build OpenCV with Java Wrapper on Ubuntu 18.04
Build Nuxt TypeScript + Vuetify environment with docker-compose
[Docker] Build Jupyter Lab execution environment with Docker
Java automated test implementation with JUnit 5 + Gradle
Launch java verification environment locally with Vagrant
Build an Ultra96v2 development environment on Docker 1
Reduce Java / Maven build time with Nexus 3 in OpenShift (okd 3.11) DevOps environment
Build TensorFlow operation check environment with Docker