[JAVA] Selenide know-how

Overview

Selenide is a Junit-based test Those that can operate the browser and check whether the expected screen transition and data are displayed

Initial setting

environment Language: JDK 1.8

Maven description

Added the following description to pom.xml

    <dependencies>
 <!-Omitted->
        <dependency>
            <groupId>com.codeborne</groupId>
            <artifactId>selenide</artifactId>
            <version>4.9</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

4.9: Latest version of Selenide as of 3/12/2018

For Spring Boot

If you have SpringBoot installed, the version of the library (Selenium) referenced by Selenide is old. The following additional settings are required to operate selenide

Additional settings
    <dependencies>
 <!-Omitted->
      <dependency>
          <groupId>org.seleniumhq.selenium</groupId>
          <artifactId>selenium-api</artifactId>
          <version>3.8.1</version>
      </dependency>
      <dependency>
          <groupId>org.seleniumhq.selenium</groupId>
          <artifactId>selenium-chrome-driver</artifactId>
          <version>3.8.1</version>
      </dependency>
      <dependency>
          <groupId>org.seleniumhq.selenium</groupId>
          <artifactId>selenium-firefox-driver</artifactId>
          <version>3.8.1</version>
      </dependency>
      <dependency>
          <groupId>org.seleniumhq.selenium</groupId>
          <artifactId>selenium-remote-driver</artifactId>
          <version>3.8.1</version>
      </dependency>

      <dependency>
          <groupId>org.seleniumhq.selenium</groupId>
          <artifactId>selenium-support</artifactId>
          <version>3.8.1</version>
      </dependency>
    </dependencies>

If it is assumed to work with FireFox and Chrome, it worked only with the above settings If you get other errors like NoClassDefFound or NoSuchMethod Check selenium-api dependencies and compare it with the locally enabled version.

Driver preparation

Preparing Chrome Driver

If the browser to be started is other than FireFox, a Driver file (an executable file that receives the contents described in Selenide and operates the browser / acquires information) is required. Get the file that suits your environment from Download Sites and place it in a location that can be specified by Junit.

Soliloquy 1 I read the article that it is unnecessary after selenide 4.8, but when I ran it, the download did not occur I have prepared the driver.
Soliloquy 2 If you deploy the driver locally, you need to check if you have execute permission for the target file. If not, you need to set the execution authority separately. Be careful when implementing in an environment other than Windows such as Docker

From driver startup to termination

Driver initial settings

Follow the steps below to set the driver. Set in the @Before method

For Chrome

    @Before
    public void before() {
        Configuration.browser = WebDriverRunner.CHROME;
 //Chromeドライバのパスを設定(ここでは設定ファイルから取得)
        System.setProperty("webdriver.chrome.driver", getClass().getResource(properties.getChromeDriver()).getPath());

        ChromeOptions chromeOptions = new ChromeOptions();
 //HeadLessモード指定
        if (properties.isHeadless()) {
            chromeOptions.addArguments("--headless");
        }
        Map<String, Object> chromePrefs = new HashMap<>();
 //PopUp表示を抑制
        chromePrefs.put("profile.default_content_settings.popups", 0);
 //ダウンロードフォルダ指定(テストメソッドごとに格納パスを切り替える等任意で設定)
        chromePrefs.put("download.default_directory", "/User/Temp/result/DownloadFile");
 //ダウンロード先指定ダイアログ表示抑制
        chromePrefs.put("download.prompt_for_download", false);
        chromeOptions.setExperimentalOption("prefs", chromePrefs);
        driver = new ChromeDriver(chromeOptions);

 //WebDriverRunnerにDriverを指定
        WebDriverRunner.setWebDriver(driver);
        
        Configuration.fastSetValue = true;
 //スクリーンショットの設定ディレクトリ設定
        Configuration.reportsFolder = "/User/Temp/result/ScreenShot"; 
    }  

Each specification is described below.

Headless mode specification

If you specify the following, you can start Chrome in Headless Mode and check it with a browser. By making this specification, automatic verification can be performed even in an environment without a UI.

            chromeOptions.addArguments("--headless");

Specifying the file download path

In the following two lines, specify the directory when downloading the file Suppresses the dialog from being displayed when downloading.

 //ダウンロードフォルダ指定(テストメソッドごとに格納パスを切り替える等任意で設定)
        chromePrefs.put("download.default_directory", "/User/Temp/result/DownloadFile");
 //ダウンロード先指定ダイアログ表示抑制
        chromePrefs.put("download.prompt_for_download", false);

It will be reflected in the following part of the Chrome setting screen Chrome設定.JPG

Screenshot save destination

Specify the save destination of the screenshot below (Screenshots will be described later)

 //スクリーンショットの設定ディレクトリ設定
        Configuration.reportsFolder = "/User/Temp/result/ScreenShot";

Character input specification

By specifying the following, when entering characters in the input tag, Specify the specified characters at once (If you do not specify the following, each character will be entered on the screen.)

        Configuration.fastSetValue = true;

Driver end

It is executed in the @After method. Exit the Driver with the following method

    @After
    public void after() {
        driver.close();
    }

test

Executed in the method of @Test (similar to normal Junit) First of all, as a rough flow

  1. Start the specified URL with Selenide # open (String, Class)
  2. Get the launched page element and check if the expected element is displayed
  3. Click the button etc. to check if the transition is as expected or if it behaves as expected You can check multiple screens by repeating "2-3".

Start the screen by specifying the URL

Open the specified page as follows

ItemsPageObject itemsPage = Selenide.open("http://localhost:8080/items", ItemsPageObject.class);

About PageObject

The class (Object) that is the second argument and the return value is called PageObject. Get and operate Selenide elements in this PageObject In the test method, it is recommended to judge the acquisition result and operate the screen via PageObject.

    @Test
    public void testItemList() throws Exception {
        ItemspageObject itemsPage = Selenide.open("/items", ItemspageObject.class);
        List<String> ret = itemsPage.getItemNames();
        assertEquals("ItemCount", 2, ret.size());
    }

In the above example, get the HTML element in ʻItemspageObject # getItemNames ()` Confirmation of the acquired contents is carried out in the Test method.

Get element

Get the element by describing the get method in Page object (class)

Basic element acquisition method

You can get the target element by using Selenide # $ (String) After that, I will describe such a simple page as an example

<table>
 <tr>
   <th>Header1</th>
 </tr>
 <tr>
   <td>Data1</td>
 </tr>
 <tr>
   <td>Data1</td>
 </tr>
</table>

You can get the whole <table> tag by getting it as follows

public SelenideElement getTable(){
   return Selenide.$("table");
}

If you want to get more child elements, you can get them as follows

public ElementsCollection getTableRows(){
 //Tableの要素を取得
   SelenideElement table = Selenide.$("table");
 //子要素の一覧を取得
   return table.$$("tr");
}

You can search for an element with the character string specified by SelenideElement Selenide # $ (String) and get the first element that was obtained.

ʻElementsCollection Selenide # $$ (String) Searches for an element with the specified character string, and can be obtained from the element list that was obtained first. Since ʻElementsCollection is an inherited class of ʻAbstarctList `, it is possible to check the number of acquired elements and check the contents of List (details omitted).

Element acquisition method (set in the PageObject field)

You can inject the element by describing the acquisition method in @ FindBy as follows for the PageObject field.

public class ItemspageObject extends SelenideBasePageObject {

    @FindBy(how = How.TAG_NAME, using = "table")
    private SelenideElement table;


    @FindBy(how = How.TAG_NAME, using = "tr")
    private ElementsCollection rows;

In the above case, when creating an instance of ʻItemspageObject, get the element from the startup page with tag name = "table" and set the value. If you want to get the child elements of the above table, you can specify your own class instead of SelenideElement`.

public class ItemspageObject extends SelenideBasePageObject {

    @FindBy(how = How.TAG_NAME, using = "table")
    private OriginalTable table;

ʻOriginalTable is created by inheriting ʻElementsContainer as follows

public class OriginalTable extends ElementsContainer {
    @FindBy(how = How.TAG_NAME,using = "tr")
    private List<TableRow> tableRows;

If you want to inject multiple elements and want to specify your own class, declare it as List <T> T must be a class that inherits ʻElementsContainer`

public class TableRow extends ElementsContainer {
    /**
     * TableHeader
     */
    @FindBy(how = How.TAG_NAME, using = "th")
    private ElementsCollection headers;

Manipulating elements

I will explain the operation for the acquired element

Get value

To get the value enclosed in the tag of the specified element Use String SelenideElement # getText ()

   <th>Header1</th>

If you execute it for Elemet corresponding to the above "th" tag, you can get the character string "Header1".

Get attribute value

To get the attribute value of the specified element, use String SelenideElement # getAttribute (String).

<a href="/users">Data2</a>

If you want to get the above "href" attribute, get it as follows.

SelenideElement aTag;
 //中略
String hrefStr = aTag.getAttribute("href");

important point In the case of href attribute, the value to be fetched is as follows http: // loalhost: 8080 / users (returns the interpreted absolute path instead of the relative path)

Set a value for an element

String setting

Specify a character string for the following character string input items

<a>Input:<input type="text" name="NAME"></a>

Implemented using SelenideElemnt # setValue (String)

 //InputTagに文字列を設定
inputTag.setValue("aaaaa");
 //設定した文字列は"SelenideElemnt#getValue()"で取得できる
assertEquals("InputTagValue", "aaaaa", inputTag.getValue());

File specification

Specify the file path for the following file selection items

<a>FileUp:<input type="file" name="FILE"></a>
 //InputTagにファイルパスを設定
fileUpTag.uploadFile(new File("/User/Text.txt"));

The file is not actually uploaded when the above process is executed, only the file path is set in the item. (Upload is done only after submitting)

Value setting for hidden items

If you want to set a value for an item with type ='hidden', you will get an error using SelenideElemnt # setValue (String). Therefore, set the value via JavaScript.

<input type='hidden' name='hiddenItem' value='HiddenValue' />

To set a value for the'hiddenItem'item in the above example, you can set the value by writing as follows.

 String value = "setting value"
((JavascriptExecutor) webDriver).executeScript("document.getElementsByName('hiddenItem').item(0).value = '" + value + "';");

WebDriver for IE / FireFox / Chrome both inherits RemoteWebDriver and Since RemoteWebDriver implements JavascriptExecutor, it can be supported by a general browser.

File download

Download process provided by Selenide

Use File SelenideElemnt # download () to get the file referenced by the href attribute. It is not possible to handle processing that includes intermediate processing such as operation with JavaScript

Download process that seems necessary

After clicking the button or link, check the download folder (specified in the settings in [Link destination](#Specify file download path)) and acquire the newly created file as a download file.

The following notes

  1. Keep the number of files in the specified folder before clicking (see [Link](#Listener Settings))
  2. When the download process is executed, the contents of the folder change in the following order, so it is necessary to finally detect the completion of file download and scrutinize the file.
  3. No file increase (download not started)
  4. The extension of the created file is .tmp (Downloading state by Chrome-1)
  5. The extension of the created file is .crdownload (Downloading state by Chrome-2)
  6. The created file name becomes a regular name (download completed)

You can get it by performing the above 1-4 judgments with the following sources.

    /**
 * Get the latest download file
     *
 * @return Latest download file
     */
    public File getLatestDownloadFile() {
        final File downloadDir = new File(getFileDownloadPath());
        
 //ダウンロード完了まで10秒待機
        WebDriverWait waitForUpload = new WebDriverWait(driver, 10);

        waitForUpload.until(new ExpectedCondition<Boolean>() {
 //ここの判定処理は、デフォルトで500ミリ秒ごとに実施
            public Boolean apply(WebDriver _driver) {
 //ファイル名からダウンロード中か否かを判定
                return isDownloadFinish(getLatestFile(downloadDir));
            }
        });

 //最新ファイルを取得
        File retFile = getLatestFile(downloadDir);
 //最新ファイルが取得できたら対象のファイルを返却
        if (isDownloadFinish(retFile)) {
            return retFile;
        }
 //出来なかったらNull返却
        return null;
    }

    /**
 * Determine if it is being downloaded
     *
 * @param target Target file
 * @return Whether the download is complete
     */
    private static boolean isDownloadFinish(File target) {
        return target != null && !target.getName().endsWith(".crdownload") && !target.getName().endsWith(".tmp");
    }

    /**
 * Get the latest file
     *
 * @param targetDir Download directory
 * @return Latest download file
     */
    private File getLatestFile(File targetDir) {
        File[] downLoaededFiles = targetDir.listFiles();
        
 //クリック前に保持したファイル数と変わっていなかったらNullを返却
         if (downLoaededFiles == null || downLoaededFiles.length == resultFileCount) {
            return null;
        }

 //ファイルの更新時間で比較し、最新のファイルを返却
        Arrays.sort(downLoaededFiles, new Comparator<File>() {
            @Override
            public int compare(File file1, File file2) {
                return file1.lastModified() <= file2.lastModified() ? 1 : -1;
            }
        });

        return downLoaededFiles[0];
    }

Listener settings

By specifying a listener (implementation class of WebDriverEventListener) for WebDriverRunner as shown below, processing can be executed at the timing before clicking.

       WebDriverRunner.addListener(new WebDriverListenerImpl());

The implementation class of WebDriverEventListener is implemented like this (hold the number of files in the specified folder before clicking)

/**
 * Implementation class of WebDriverEventListener
 */
public class WebDriverListenerImpl implements WebDriverEventListener {

 //その他のメソッド群は省略

    @Override
    public void beforeClickOn(WebElement webElement, WebDriver webDriver) {
 //クリック前に実績フォルダ配下のファイル数を取得する
        baseClass.setResultFileCount(new File(" "/User/Temp/result/DownloadFile"").listFiles().length);
    }
}


Recommended Posts

Selenide know-how
Selenide option list