~~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.
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.
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 |
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.
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.
java -jar selenium-server-standalone-3.141.59.jar -role hub
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
}
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
The directory structure may change, but for the time being, I made it with the following structure.
--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.
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
*********************************/
}
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.
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++;
}
}
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");
}
}
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
I don't think I've had much trouble with the operation I want to do: hugging: However, IE and Teme are not good.
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).
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;
}
}
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.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:
NodeConfigBrowser.json
{
"capabilities": [
{
"platform": "WINDOWS",
"browserName": "internet explorer",
"maxInstances": 1,
"seleniumProtocol": "WebDriver"
}
],
"hub": "http://XXXXX:4444/grid/register",
"register": true
}
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.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