karateでWebUIのテストをする。seleniumとのソース比較も

# はじめに
[前回はkarateを使用してREST APIのテストを書きました。](https://qiita.com/mink0212/items/872893202449a0a42776)次はWebUIのテストの書き方をまとめます。REST APIの書き方とほとんど同じなので、前回と今回の2つを合わせるとkarateの魅力を感じられると思います。

# 環境
 - IntelliJ：2020.1.3
 - Java:11
 - Gradle:7.0

# 環境作成
[前回の環境作成](https://qiita.com/mink0212/items/872893202449a0a42776#%E7%92%B0%E5%A2%83%E4%BD%9C%E6%88%90)と同じなので、そちらを確認してください。

# テストソースの作成
前回と同様karateを使用するので、必要なものはJavaファイルとfeatureファイルの2つで変わりません。

## Javaファイルの作成
Javaファイルは前回とほとんど同じです。ただ、今回はテスト前に環境変数をリセットするようにしていますが、あまり関係ないです。（最終的には設定ファイルで設定するはずです）

``` java
package com.karate.ui;
import com.intuit.karate.junit5.Karate;
import org.junit.jupiter.api.BeforeEach;

public class LoginPage {
    @BeforeEach
    public void beforeClass() {
        // 環境変数をリセットする
        System.setProperty("karate.env", "mock");
    }
    @Karate.Test
    Karate loginPageTest() {
        // loginPage.featureを読み取るように定義
        return Karate.run("loginPage").relativeTo(getClass());
    }
}
```

## featureファイルの作成
次にfeatureファイルを作成します。ここが、前回と最も違うところです。
### Webブラウザの設定
WebUIをテストするので、ブラウザを開くための設定を``` configure driver ```にします。下の例ではchromeを使用すること以外は設定していないのですが、``` { type: 'chrome', headless: true } ```と設定するとヘッドレスモードで起動します。他にも様々な設定があるので[公式](https://intuit.github.io/karate/karate-core/#driver-configuration)を見てみてください。

``` Gherkin
Feature: UI操作
  # driverの設定. chromeを設定
  Background:
    * configure driver = { type: 'chrome' }

```

### テストシナリオの作成
ブラウザの設定が出来たので次はテストシナリオを描いていきます。書き方としては、前回と同様Gherkin形式で記載していきます。違う点はdriverにURLを指定する点、それぞれの処理をWebUI用の関数経由を使用する点の2つです。

``` Gherkin
Feature: UI操作
  # driverの設定. chromeを設定
  Background:
    * configure driver = { type: 'chrome' }

  Scenario: loginPage start
    # URLを入力
    Given driver 'http://localhost:3000'

    # テキスト入力
    And input('#my-account', 'dummy')
    And input('#my-password', 'dummy')
    # select選択（material-uiを使用しているので、karateのselectは使用できない）
    And mouse('#simple-select').click()
    # selectが開いた状態で下ボタンとエンターを押す。（bodyは特に対象を指定せずにWeb画面全体をさす）
    And input('body', [Key.DOWN, Key.ENTER])
    # クリックする
    When mouse('[name=commit]').click()
    # 画面遷移後にメッセージ文字列が表示されるかチェックする
    Then match text('.MuiInputLabel-root') contains 'メッセージ'
```

# テストの実行
テストの実行方法は[前回](https://qiita.com/mink0212/items/872893202449a0a42776#%E3%83%86%E3%82%B9%E3%83%88%E3%81%AE%E5%AE%9F%E8%A1%8C)と同じです。

### テスト結果
実行するとブラウザが開いて自動的にWeb操作が行われます。
全ての結果がOKならこのようにpassedとなります。

![1無題.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/481713/e0758dd5-36e6-7b9b-a42f-779fbd2cd5db.png)


# seleniumとの比較
## それぞれのソースコード
### selenium
例で書いたのはユーザ名とパスワードを入力して画面遷移するソースを書きました。

``` java
package sample.selenium.test;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;

import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
import org.mockserver.client.MockServerClient;
import org.mockserver.junit.MockServerRule;
import org.mockserver.model.Header;
import org.mockserver.model.HttpRequest;
import org.mockserver.model.HttpResponse;
import org.openqa.selenium.Alert;
import org.openqa.selenium.By;
import org.openqa.selenium.Keys;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
public class TestProfile {
	private static WebDriver driver = null;

	@BeforeClass
	   public static void setUpBeforeClass() throws Exception {
		System.setProperty("webdriver.chrome.driver", "driver/chromedriver.exe");

		ChromeOptions options = new ChromeOptions();
		driver = new ChromeDriver(options);
	}

	@Before
	   public void setUpBefore() throws Exception {
		driver.get("http://localhost:3000");
	}

	@AfterClass
	   public static void tearDownAfterClass() throws Exception {
		driver.close();
	}

	@Test
	public void loginCheck() {
		WebElement myAccount = driver.findElement(By.id("my-account"));
		myAccount.sendKeys("user");
		WebElement myPassword = driver.findElement(By.id("my-password"));
		myPassword.sendKeys("pass");
		WebElement buttonElement = driver.findElement(By.id("input-button"));
		buttonElement.sendKeys(Keys.ENTER);
		String url = driver.getCurrentUrl();
		assert(url).contains("http://localhost:3000/message");
	}
}
```

### karate
例で書いたのはユーザ名とパスワードを入力して画面遷移するソースを書きました。

``` Gherkin
Feature: UI操作
  Background:
    * configure driver = { type: 'chrome' }

  Scenario: loginPage start
    Given driver 'http://localhost:3000'

    And input('#my-account', 'dummy')
    And input('#my-password', 'dummy')
    When mouse('[name=commit]').click()
    Then waitForText('body', 'メッセージページ')
    Then match driver.url == 'http://localhost:3000/message'
```

## 感想
seleniumは53行、karateは10行と行数は圧倒的にkarateの方が少なく書けました。また、ソースの見やすさもkarateの方が上なのかなと思います。一方でseleniumは昔からあるライブラリなので情報が豊富にありますが、karateはそこまで情報はありません。しかし、使い方を見ればわかるようにkarateは簡単に使えるので使うのであればkarateを使用した方が見やすく早くソースがかけるとおもいます。

# 終わりに
karateでWebUIのテストを書く方法をまとめました。結論としては、使用する関数が違うだけでREST APIのテストとほぼ書き方が変わらないことがわかりました。そのため、REST APIのテストとWebUIのテストをkarateにすることで学習するライブラリが1つで済み、ソース間で書き方の統一が図れてバックエンド用のテスター・フロントエンド用のテスターなど不合理な分け方をせずに、バックエンド・フロントエンド両方のテスターとして人を使えるので人に流動的に作業を割り当てが出来そうです。