Kotlin×Selenideを利用してブラウザ自動テストをしてみる

# 前提
せっかくKotlinに触れ始めたので、ブラウザ自動テストを作成したいと思います。

# 環境
- macOS: 11.6.4
- IntelliJ IDEA: 2021.2.3 (Community Edition)
- Kotlin JVM: 1.6.21 
- Gradle: 7.4.2
- Java: temurin 17.0.3

# Selenideについて
https://www.ossnews.jp/oss_info/Selenide#:~:text=Selenide(%E3%82%BB%E3%83%AC%E3%83%8B%E3%83%89%2F%E3%82%BB%E3%83%AC%E3%83%8A%E3%82%A4%E3%83%89)%E3%81%A8,%E3%83%86%E3%82%B9%E3%83%88%E8%87%AA%E5%8B%95%E5%8C%96%E3%83%95%E3%83%AC%E3%83%BC%E3%83%A0%E3%83%AF%E3%83%BC%E3%82%AF%E3%81%A7%E3%81%99%E3%80%82

>Selenide(セレニド/セレナイド)とは、「Selenium WebDriver」を利用したテスト自動化フレームワークです。　Selenideは、読みやすく保守しやすい自動テストをJavaで記述できるフレームワークです。「シンプルAPI」「Ajaxサポート」「強力なセレクター」「簡単な設定」などの特徴があります。`


# 手順
## 新しいプロジェクトを作成する
まずはIntelliJを利用して新規プロジェクトを作成します。

#### プロジェクト作成
IntelliJで`File > New > Project...`を選択します。

#### 設定を入力する
以下の情報を入力します。その後Nextボタンをクリックします。
<img width="560" alt="" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2433961/1bcbfb15-ee78-a171-0bd6-715b79a73391.png">
 - Name:`selenide-sample`
 - Project Template:`Application`
 - Build System:`Gradle Kotlin`
 - Project JDK:`temurin-17`

<img width="560" alt="" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2433961/3d3d121a-dfd6-aa1c-7236-276b16b68636.png">
Target JVM versionは`1.8`では良くない気がしますが、選択肢が13までしかなかったので一旦このままにしておきます。
Finishボタンをクリックするとプロジェクトが作成されます。

##  Gradleの設定
以降は以下のサイトを参考にしていきます。

https://selenide.org/quick-start.html

`build.gradle.kts`のdependenciesに一行追加します。
```build.gradle.kts
dependencies {
  testImplementation("com.codeborne:selenide:6.6.6")
}
```

ついでに色々バージョン関連を整えます。
```build.gradle.kts
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

plugins {
    kotlin("jvm") version "1.6.21"
}

group = "com.example"
version = "1.0-SNAPSHOT"
java.sourceCompatibility = JavaVersion.VERSION_17

repositories {
    mavenCentral()
}

dependencies {
    testImplementation(kotlin("test"))
    testImplementation("com.codeborne:selenide:6.6.6")
}

tasks.test {
    useJUnitPlatform()
}

tasks.withType<KotlinCompile>() {
    kotlinOptions.jvmTarget = "17"
}
```

gradleでライブラリを引っ張ってきます。

## テストを書く
SelenideのQuickStartにGoogle検索のサンプルがあるとのことだったので、そちらを参考にして作成してみました。

https://github.com/selenide-examples/google/blob/master/test/org/selenide/examples/google/selenide_page_object/GoogleTest.java

#### テストクラスの作成
`com/example/selenium_sample/GoogleTest.kt`
```kotlin:GoogleTest.kt
package com.example.selenium_sample

import com.codeborne.selenide.CollectionCondition.sizeGreaterThan
import com.codeborne.selenide.Condition.*
import com.codeborne.selenide.Selenide.*
import org.junit.jupiter.api.Test
import org.openqa.selenium.By

class GoogleTest {

    @Test
    fun `Search in Google`() {
        open("https://duckduckgo.com")
        `$`(By.name("q")).`val`("selenide java").pressEnter()

        `$$`(".results .result").shouldHave(sizeGreaterThan(1))
        `$`("#r1-0").shouldHave(text("Selenide: concise UI tests in Java"))
    }

}
```
コード内の`#`や`.`でidやclassを指定しています。
<img width="560" alt="" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2433961/5c67712b-64d9-2109-7fa9-15bcfcfaa4f7.png">

#### テスト実行
画面が立ち上がり、実際の検索結果が表示されました。ただテストは失敗しました。

思ったより検索結果が取れなかった、とかでしょうか。
今回はサンプルなので、こちらはあきらめます。
```console:console
List size mismatch: expected: > 1, actual: 1, collection: .results .result
Elements: [
	<div class="result result--more" id="rld-1">結果をさらに表示</div>
]
```

## エラー解決
テストは実行されているのですが、コンソールに以下のエラーが発生しました。
```console:console
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
```

#### `build.gradle.kts`を修正
こちらを参考にしました。

https://stackoverflow.com/questions/7421612/slf4j-failed-to-load-class-org-slf4j-impl-staticloggerbinder

dependenciesに2行追加しました。
```build.gradle.kts
dependencies {
    testImplementation("org.slf4j:slf4j-api:2.0.0-alpha7")
    testImplementation("org.slf4j:slf4j-simple:2.0.0-alpha7")
}
```

結果、エラーは表示されなくなりました。

## テスト終了後にブラウザが消える
テスト終了後ブラウザが消えてしまっては動作を確認しにくいですね。テスト終了後、ブラウザを残しておきたいと思います。

#### `Configuration`の設定
どうやら`com.codeborne.selenide.Configuration`クラスのプロパティを設定することでテストの動作を変えることができそうです。
それらしいプロパティ`holdBrowserOpen`を見つけました。

Javadocにはこのように書かれています。
>If holdBrowserOpen is true, browser window stays open after running tests. It may be useful for debugging. Can be configured either programmatically, via selenide.properties file or by system property "-Dselenide.holdBrowserOpen=true". Default value: false.

このプロパティをtrueにしておくと、テスト終了後もブラウザを開いたままにします。これはデバッグに役立つかもしれません。プログラムからも、`selenide.properties`からも、またJVM引数からも設定することができます。デフォルトはfalseです。

#### テスト実行前にこのプロパティをtrueにしておく
テストクラスを以下のように変更してみました。
```kotlin:GoogleTest.kt
package com.example.selenium_sample

import com.codeborne.selenide.CollectionCondition.sizeGreaterThan
import com.codeborne.selenide.Condition.*
import com.codeborne.selenide.Configuration
import com.codeborne.selenide.Selenide.*
import org.junit.jupiter.api.BeforeEach![スクリーンショット 2022-07-03 22.51.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2433961/8d44122b-27da-a846-03a1-27dba95bd0ae.png)

import org.junit.jupiter.api.Test
import org.openqa.selenium.By

class GoogleTest {

    @BeforeEach
    fun `Set Configure Before Test`() {
        Configuration.holdBrowserOpen = true
    }

    @Test
    fun `Search in Google`() {
        open("https://duckduckgo.com")
        `$`(By.name("q")).`val`("selenide java").pressEnter()

        `$$`(".results .result").shouldHave(sizeGreaterThan(1))
        `$`("#r1-0").shouldHave(text("Selenide: concise UI tests in Java"))
    }

}
```

今度はブラウザが消えずに残ってくれました👏
<img width="600" alt="" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2433961/1da35738-b271-3cc0-df9b-7fbda1155745.png">

## Chrome以外で動かしたい
Chrome以外のブラウザでテストをしたいということもあると思います。そのときはどうすれば良いでしょうか。
こちらも`com.codeborne.selenide.Configuration`クラスのプロパティを設定することで変更が可能なようです。`browser`プロパティには以下のように記載があります。
> Which browser to use. Can be configured either programmatically, via selenide.properties file or by system property "-Dselenide.browser=ie". Supported values: "chrome", "firefox", "ie", "opera", "edge" Default value: "chrome"

どのブラウザをしようするか、プログラムからも、`selenide.properties`からも、またJVM引数からも設定することができます。サポートされている値は`chrome`/`firefox`/`ie`/`opera`/`edge`で、デフォルトは`chrome`。

こちらをsafariに変更してみます。定数も用意されていました。

```kotlin:GoogleTest.kt
package com.example.selenium_sample

import com.codeborne.selenide.Browser
import com.codeborne.selenide.Browsers
import com.codeborne.selenide.CollectionCondition.sizeGreaterThan
import com.codeborne.selenide.Condition.*
import com.codeborne.selenide.Configuration
import com.codeborne.selenide.Selenide.*
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.openqa.selenium.By

class GoogleTest {

    @BeforeEach
    fun `Set Configure Before Test`() {
        Configuration.holdBrowserOpen = true
        Configuration.browser = Browsers.SAFARI
    }

    @Test
    fun `Search in Google`() {
        open("https://duckduckgo.com")
        `$`(By.name("q")).`val`("selenide java").pressEnter()

        `$$`(".results .result").shouldHave(sizeGreaterThan(1))
        `$`("#r1-0").shouldHave(text("Selenide: concise UI tests in Java"))
    }

}
```

ただこのままでは動きませんでした。
```console:console
Could not start a new session. Possible causes are invalid address of the remote server or browser start-up failure.
```

#### safari側の設定
リモートオートメーションを許可にチェックを入れます。
<img width="600" alt="" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2433961/2580da55-3616-c280-0444-9c8dd0c668bf.png">

#### 再度テスト実行
<img width="600" alt="" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2433961/4b7b0685-c15a-1040-eb4b-0ff7ebe027a6.png">

safariの場合、デフォルトでwebdriverが存在するようです。（パス:
`/usr/bin/safaridriver`）。
他のブラウザを使用する場合は、自分でダウンロードする等の手間が発生するかもしれません。

seleniumのこのページが参考になりそうです。

https://www.selenium.dev/ja/documentation/webdriver/getting_started/install_drivers/

# さいごに
テストを起動するだけでしたら簡単ですね。ただ、ページオブジェクト等を用意してさらに綺麗にしていくとなると最初は大変かもしれません。しかし、手動でテストをくりかえすよりかは良いのではないかと思います。