Selenium勉強会@サイボウズに行ってきました #selenium_cybozu
の記事で
Selenideの話
Selenide == Javaで書かれたSeleniumクライアントラッパー
というのを見つけてGroovy製のGebと比較したいなと思い、使ってみました。
Selenideについて
公式はここ。http://selenide.org/
Selenide is a wrapper for Selenium WebDriver that brings the following advantages:
Concise API for tests
Ajax support
True Page Objects
jQuery-style selectors
You don't need to think how to shutdown browser, handle timeouts or write monstrous code!
Concentrate on business logic!
gradle dependencies
を使って、Selenideの依存関係を確認してみると各種driverも入ってることがわかる。
+--- com.codeborne:selenide:2.23
| +--- com.google.guava:guava:18.0
| +--- org.seleniumhq.selenium:selenium-java:2.47.1
| | +--- org.seleniumhq.selenium:selenium-chrome-driver:2.47.1 -> 2.48.2
| | | \--- org.seleniumhq.selenium:selenium-remote-driver:2.48.2
| | | +--- cglib:cglib-nodep:2.1_3
| | | +--- com.google.code.gson:gson:2.3.1
| | | +--- org.seleniumhq.selenium:selenium-api:2.48.2
| | | | +--- com.google.guava:guava:18.0
| | | | +--- com.google.code.gson:gson:2.3.1
| | | | \--- org.apache.httpcomponents:httpclient:4.5.1 (*)
| | | +--- org.apache.httpcomponents:httpclient:4.5.1 (*)
| | | +--- com.google.guava:guava:18.0
| | | +--- org.apache.commons:commons-exec:1.3
| | | +--- net.java.dev.jna:jna:4.1.0
| | | \--- net.java.dev.jna:jna-platform:4.1.0
| | | \--- net.java.dev.jna:jna:4.1.0
| | +--- org.seleniumhq.selenium:selenium-edge-driver:2.47.1
| | | +--- org.seleniumhq.selenium:selenium-remote-driver:2.47.1 -> 2.48.2(*)
| | | +--- commons-io:commons-io:2.4
| | | \--- org.apache.commons:commons-exec:1.3
| | +--- org.seleniumhq.selenium:selenium-firefox-driver:2.47.1
| | | +--- org.seleniumhq.selenium:selenium-remote-driver:2.47.1 -> 2.48.2(*)
| | | +--- commons-io:commons-io:2.4
| | | \--- org.apache.commons:commons-exec:1.3
| | +--- org.seleniumhq.selenium:selenium-ie-driver:2.47.1
| | | +--- net.java.dev.jna:jna:4.1.0
| | | +--- net.java.dev.jna:jna-platform:4.1.0 (*)
| | | \--- org.seleniumhq.selenium:selenium-remote-driver:2.47.1 -> 2.48.2(*)
| | +--- org.seleniumhq.selenium:selenium-support:2.47.1
| | | \--- org.seleniumhq.selenium:selenium-remote-driver:2.47.1 -> 2.48.2(*)
| | \--- org.seleniumhq.selenium:selenium-leg-rc:2.47.1
| | \--- org.seleniumhq.selenium:selenium-remote-driver:2.47.1 -> 2.48.2(*)
| \--- commons-codec:commons-codec:1.10
環境
selenide2.23
Java8
SpringBoot1.2.7
Doma2.5.0
H2
Gradle2.8
「SpringBoot+Doma2+Gradleを試してみた。」のアプリを、ライブラリを最新化して使いまわしてます。
参考までにGebで実装したものはこちら。
「SpringBootで作ったRestAPI/Webアプリのテストを書いてみた」
実装
ソースはこちら。
https://github.com/nyasba/domaboot.git
Gebのときと同様PageObjectパターンで実装してみました。
メインページのPageObject
お作法はGebと微妙に違いますが、そんなに困ることなく移植できました。
package com.example.web.selenide.page;
import com.codeborne.selenide.Selenide;
import org.openqa.selenium.By;
/**
* メインページ
*/
public class MainPage {
private static final String URL = "http://localhost:8888/customers";
public static void open(){
Selenide.open(URL);
}
public static String title(){
return Selenide.title();
}
public static int 登録件数(){
return Selenide.$$(Selenide.$(By.id("customer-list")).find("tbody"),"tr").size();
}
public static void 姓は(String lastName){
Selenide.$(By.id("lastName")).setValue(lastName);
}
public static void 名は(String firstName){
Selenide.$(By.id("firstName")).setValue(firstName);
}
public static void で登録する(){
Selenide.$(By.id("register")).click();
}
public static String 名前(int index){
return Selenide.$(By.id("lastName" + String.valueOf(index))).getText()
+ Selenide.$(By.id("firstName" + String.valueOf(index))).getText();
}
public static void 編集ボタンを押す(int index){
Selenide.$(By.id("edit" + String.valueOf(index))).click();
}
public static void 削除ボタンを押す(int index){
Selenide.$(By.id("delete" + String.valueOf(index))).click();
}
}
編集ページのPageObject
package com.example.web.selenide.page;
import com.codeborne.selenide.Selenide;
import org.openqa.selenium.By;
/**
* 編集ページ
*/
public class EditPage {
public static String title(){
return Selenide.title();
}
public static void 姓は(String lastName){
Selenide.$(By.id("lastName")).setValue(lastName);
}
public static void 名は(String firstName){
Selenide.$(By.id("firstName")).setValue(firstName);
}
public static void で更新する(){
Selenide.$(By.id("submit")).click();
}
public static void やっぱり戻る(){
Selenide.$(By.id("goToTop")).click();
}
}
テストシナリオ
流れ。
-
@BeforeClassでテスト対象アプリを起動し、Chromeを使う設定を追加
(デフォルトはFirefoxになり、Firefoxだとdriverの設定は不要) - @Testでテスト実行
- @Afterでテストで登録したデータをすべて削除
- @AfterClassでブラウザを閉じる
package com.example.web.selenide;
import com.codeborne.selenide.Configuration;
import com.codeborne.selenide.WebDriverRunner;
import com.example.App;
import com.example.web.selenide.page.EditPage;
import com.example.web.selenide.page.MainPage;
import org.junit.*;
import org.junit.runners.MethodSorters;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
public class CustomerWebTest {
@BeforeClass
public static void setUpClass() {
// テスト対象アプリの起動
App.main(new String[]{
"--server.port=8888",
"--spring.profiles.active=test"
});
// 何も指定しない場合はFirefoxになる
Configuration.browser = WebDriverRunner.CHROME;
System.setProperty("webdriver.chrome.driver", "driver/chromedriver.exe");
}
@After
public void tearDown() {
// 登録されてるデータを全部消す
for(int i = MainPage.登録件数(); i > 0; i--){
System.out.println("削除" + String.valueOf(i));
MainPage.削除ボタンを押す(i);
}
}
@AfterClass
public static void tearDownClass() {
WebDriverRunner.closeWebDriver();
}
@Test
public void 剛田たけしとどらえもんを登録する() {
MainPage.open();
assertThat(MainPage.登録件数(), is(0));
MainPage.姓は("剛田");
MainPage.名は("たけし");
MainPage.で登録する();
assertThat(MainPage.登録件数(), is(1));
assertThat(MainPage.名前(1), is("剛田たけし"));
MainPage.姓は("どら");
MainPage.名は("えもん");
MainPage.で登録する();
assertThat(MainPage.登録件数(), is(2));
assertThat(MainPage.名前(2), is("どらえもん"));
}
@Test
public void 剛田たけしで登録した後にきれいなジャイアンに変更する() {
MainPage.open();
MainPage.姓は("剛田");
MainPage.名は("たけし");
MainPage.で登録する();
MainPage.編集ボタンを押す(1);
EditPage.姓は("きれいな");
EditPage.名は("ジャイアン");
EditPage.で更新する();
assertThat(MainPage.title(), is("顧客管理システム"));
assertThat(MainPage.名前(1), is("きれいなジャイアン"));
}
@Test
public void 剛田たけしで登録した後に変更しようとしてやっぱり戻る() {
MainPage.open();
MainPage.姓は("剛田");
MainPage.名は("たけし");
MainPage.で登録する();
MainPage.編集ボタンを押す(1);
EditPage.やっぱり戻る();
assertThat(MainPage.名前(1), is("剛田たけし"));
}
@Test
public void 剛田たけしで登録した後に削除する() {
MainPage.open();
MainPage.姓は("剛田");
MainPage.名は("たけし");
MainPage.で登録する();
assertThat(MainPage.登録件数(), is(1));
MainPage.削除ボタンを押す(1);
assertThat(MainPage.登録件数(), is(0));
}
}
Gebでは各テストをつなげて1つのテストシナリオにしていたのですが、JUnitでテストの順序を指定する方法がうまくいきませんでした。(@FixMethodOrderとか試したのですが)
ただ、そこは本質じゃないので、それぞれのテストを独立したものに書き換えたことによりGebと構成は少し変わってます
感想
SelenideはJava製ということがあって、Javaエンジニアの方が気軽に使えるというのが一番いいところですね。Groovy(Spock)の知識が必要なGebと比べると敷居は低いと思います
簡単なところまでしか試していませんが、機能的にも遜色ないとおもいます。
(もしかすると何か解決方法があるのかもしれませんが)Gebだとページが遷移していくときにIDEの補完機能が効かないときがあり、Selenideはそういうことはありませんでした。
#個人的にはSpock+SelenideでCustomerWebTestの可読性をあげるのもアリかなと思いました。
おわり。