DeNA Testing Blog

Make Testing Fun, Smart, and Delighting End-Users

UIテストの最前線: SeleniumConf Berlin 2017参加レポート

SWETグループの薦田です(@toshiya_komoda)。10月9、10日にドイツ・ベルリンで開催されたSelenium Conferenceに参加してきました。私もLightning Talksで、機械学習とUIテストに関する発表をさせていただきました(スライド)。

こちらの内容については、別の記事で書かせていただくこととし、この記事ではカンファレンスで聴講し、特に気になったトークについてレポートさせていただこうと思います。

ウェブ・アプリケーションにおけるUIテストの最前線の状況が少しでも伝われば幸いです。

f:id:swet-blog:20171018220430j:plain

Seleniumについておさらい

Selenium WebDriverは、ウェブ・ブラウザをプログラムから操作するためのソフトウェアツールです。OSSとして開発されており、主な用途としてウェブ・アプリケーションのテスト自動化に用いられています。現バージョンは3.6.0(2017年10月24日現在)、プロジェクト開始から10年以上の歴史を持ちます。

Selenium内部ではブラウザを操作するためにWebDriverと呼ばれるAPIが用いられています。主要なブラウザベンダが、自社ブラウザ向けのWebDriverの実装を提供しています(Official Selenium Blog: Selenium 3.0: Out Now)。 Seleniumはブラウザベンダが公式に認めているブラウザ・テストツールと言って言い過ぎではないでしょう。また、W3Cにて仕様標準化の作業が進められています(WebDriverのCR)。

Selenium Conference

Selenium Conferenceはプロジェクト公式のミートアップイベントで、プロジェクトのリーダーであるステアリング・コミッティも参加するイベントです。7年の歴史があり、2011年から2015年は年に1度、2016年からは年に2度ずつ開催されています。 前回のカンファレンスは、2017年4月にアメリカ オースティンで開催されており、SWETでも参加レポートを公開しています。 また、過去のトークがYouTube上で視聴できるようになっているので、興味のある方は眺めてみると雰囲気が伝わるかと思います。 今回のトークもすでにアップロードされています。

それでは、特に気になったトークを3つ紹介させていただきます。

Selenium State of the Union, To Infinity and Beyond Selenium

ステアリング・コミッティの一人であるSimon Stewart氏による基調講演です。 基調講演の中で、Seleniumが普及しプロダクトが成熟していく中で現在のSeleniumに求められているものは、 より分かりやすいドキュメンテーションと分かりやすいAPIだ、と語りました。 これらを実現するために、現役でテストを書いているエンジニアにこそ、OSSであるSeleniumにコントリビュートしてほしいとのことでした。

また気になる話題として、Firefoxのバージョンアップに伴い2017年8月にサポートが切れてしまった Selenium IDEの動向についても言及がありました。 現在、applitoolsの開発者が中心となって、Selenium IDEを公式に作り直しているとのことで期待して待っていて欲しいとのことでした(開発中のSelenium IDEのレポジトリ)。

Keep Your Test Lean

GithubのQAエンジニアによるUIテストにおけるベストプラクティスの話です。 UIテスト自動化が効果的に機能するための要件として、

  • Valuable: 重要で実行頻度の高いテストケースが自動化されている
  • Reliable: UIテストの実行結果を安定させるための仕組みが備わっている
  • Fast: テスト実行時間が短い時間に収まっている

という3点を挙げ、UIテストスイートがこれらを満たすために必要な戦略と工夫について説明されています。

UIテストをやみくもに作成してしまった結果、メンテナンスコストばかり大きくなりメリットが感じられなくなった、という話は日本でもよく聞く話です。 SWETでもこれまでにUIテストを運用していく中で、全く同じような運用上の課題を経験しています(資料)。

特にUIテストをテスト担当者に属人化させず、システム開発者から目に見えるものとするための工夫が興味深いものでした。具体的には、

  • UIテストはシステムの開発言語と同じ言語で記述する
  • テスト対象システムと同じコードベースで管理する
  • テストコードベースの運用ルールをテスト対象システムと同じにする
  • テスト対象UIパーツにカスタムデータ属性を埋め込んでおく

といった工夫が紹介されていました。最後のテスト対象のUIパーツにカスタムデータ属性を埋め込むとは、例えば以下のHTMLにおけるdata-qaがこれに相当します。 このタグがついたUIパーツは、テストで利用されているということが開発者にひと目で分かります。これにより、意図せずテストを壊す変更が入ってしまうことを防止できるというわけです。

# コードは動画中のスライドより引用
<l-popup as="popup" class="has-menu tall-option has-icon cap-option">
  <ul class="popup-menu narrow-menu" data-qa='add-candidate-menu'>
    <li class="option">
      <a on-click="addManually()">
        <view is="icon-edit"></view>
        Add manually
      </a>
    </li>

また、非同期処理が原因でテストが落ちた場合の再実行の仕組みを整える、並列化やヘッドレスブラウザを利用して高速化を行う、といったテスト実行環境の足回りの改善の重要性にも触れています。

UIテスト導入を検討している方に、ぜひ一度視聴してほしい内容でした。

Readable. Stable. Maintainable. E2E Testing @ Facebook

Facebookでテストフレームワーク・テストインフラを担当しているエンジニアの発表です。

Seleniumをそのまま用いたUIテストコードの可読性が低いことは、よく指摘されるSeleniumの問題点ですが、Facebookの中で開発しているテストフレームワークがこの問題をいかに解決しているかについて、具体例を用いて説明しています。

f:id:swet-blog:20171020201722p:plain (写真は動画中より抜粋)

例えば、上記のようなフィード欄のUIテストを考えてみましょう。以下の例では、 Seleniumをそのまま使って書かれたコードが、可読性の低いテストコードになる様子を分かりやすく説明しています。

# コードは動画中のスライドより引用

var feed
// feedがロードされるのが非同期なので、これに対応するためのwhileループ
while (feed == nil) {
  feed = try? driver.findElement(by: id("feed"))
}

// id を直接指定したWebElementの取得
let story = element.findElementBy(by: id("permalink"))[2]

// たまに一回目のクリックがうまくいかないのでretryしている
story.click()
story.click()

// 理由は分からないが、sleepを追加するとテストが動作するので入れられたsleep
sleep(5000)

// id を直接したUIパーツのセレクト
let postText = driver.findElement(by: id("post_text"))

コメントに記載している通り、UIテストにまつわるバッドノウハウがそこかしこに登場しており、本質的にテストしたい内容が、表現されている実装とは言えないものとなってしまっています。 FacebookにおけるUIテストフレームワークの開発の動機の一つは、この問題を解決し、可読性が高くメンテナンスが容易なUIテストコードを実現することだったそうです。

FacebookのUIテストフレームワークにおける問題解決のポイントは、テスト対象画面上のUIパーツの構造とそれに紐づくアクションをクラスとして定義する仕組みを提供していることです。

例えば、前述のフィードのUIは以下のようなクラスとして定義されると説明されています。

# コードは動画中のスライドより引用

// フィードに対するUI定義クラス。
// 子要素と、それに対するアクションを定義していく
class FeedStory: Component {
  let definition = 
           root(storyLocator, "story")
           .withDescendant(
             profilePhotoLocator,
             "profile photo"
           ) // 画像アイコン
           .withDescendant(
             permalinkLocator,
             "permalink"
           ) // その下にフィード詳細へのパーマリンク
           .withDescendant(
             contentLocator,
             "post content"
           ) // その下に投稿内容
           .withDescendant(
              StoryFooter
           ) // その下にフッタ
             // 下部で定義されたStoryFooterクラスを使いまわすことができる

  // UIパーツ内の要素を取得する関数
  getPostText() -> String {
    return getTextOf("post content")
  }

  // UIパーツに対するアクションを定義した関数。
  // 返り値はPageObject Patternの考え方に従い、UIオブジェクトが返される
  openProfile() -> UserProfile {
    return click("profile photo", andGoTo: UserProfile)
  }
  ...
}

// Like, Commentを含むフッターの定義。StoryFeedクラスと同様にUIを定義していく
class StoryFooter {
  // Like, Commentを含むFooterコンポーネントのUI定義クラス
}

このようなUIパーツのクラスを利用することで、 例えば「フィードを投稿して、この投稿したフィードの詳細ページを開き、これにコメントを付ける」といった複雑なシナリオテストも、以下のように簡略に書けるようになるとのことです。

waitFor(LoginScreen)
  .loginAs(user)
  .selectComposer()
  .open()
  .enterText("Test Post")
  .post()
  .selectStoryWithText("Test Post")
  .openPermalink()
  .postComment("Test Comment")

冒頭のバッドノウハウだらけのテストコードと比較して、テストコードの可読性が大きく向上していることが伺えます。

はじめにテスト対象のUIの仕様をクラスとして定義してしまうことで、テストロジックのメンテナンスコストを下げるというアイディアは、UIテストのデザインパターンとしてよく知られたPageObjectパターンの考え方をフレームワークとして一歩推し進めたかたちとなっており、興味深いアプローチだと感じました。

また、トークの中ではテストコードの可読性に関する話題だけでなく、One Worldと呼ばれるモバイルテスト基盤の話など、UIテストに関する多くの技術トピックに触れています。

UIテストの技術的な側面に興味のある方には是非視聴していただきたい内容でした。

終わりに

講演以外で印象に残っていることは、ZaleniumというOSSソフトウェアの開発者と直接会って話ができたことです。 Zaleniumは、Seleniumを用いた複数のUIテストを並列実行することができるテスト基盤を提供するソフトウェアです。 SWETでもZaleniumを利用しており、機能拡張のPRを送っていました。 普段は会えないOSS開発者と直接会って話ができることは海外カンファレンス参加の一つの魅力だと感じました。

Selenium Conferenceは年2回、来年はインドとアメリカでの開催です。 来年4月には第1回目のAppium Conferenceも開催されます。 DeNA Testing Blogでは今後もテスト自動化の最前線の動向について、継続して情報発信していく予定です。

参考資料

Android Test Night #1 開催

はじめまして、SWETの金子(theoden9014)です。

今回のエントリーは2017-09-21に第一回目としてSWET主催で開催したAndroid Test Night #1と、そこで「Android SDK with Docker」という題で私が発表した内容についてご紹介したいと思います。

Test Night とは?

Test Nightとは、SWETが主催する主にテストに関係することについて語り合うことを目的とした勉強会です。

昨年11月からiOS Test Nightと呼ばれるiOSに特化したTest Nightを2ヶ月ごとに開催しています。 これまで合計39件の発表がおこなわれ、多数の方に参加していただき、活発な議論がおこなわれる勉強会になっています。

Android Test Night とは?

iOS Test Nightと同様にTest Nightで管理しているイベントです。 今回はAndroidについても同様にテストを対象としたイベントの需要があることがわかり、開催する運びとなりました。

Android Test Night #1

今回はその第1回目です。
15分枠のセッションでは

  • 「コードレビューをより良くする Danger x Android」
  • 「Androidのテストを効率的にするために考えたこと」
  • 「Android E2E Testing at Mercari」

といった、開発プロセス改善についてのセッションや、実際にテストに取り組んでいる事例紹介のセッション。
5分枠のセッションでは

  • 「AndroidSDK with Docker」
  • 「JUnit5とAndroidのテスト」
  • 「Kotlinで書かれたAndroidアプリをBazelでビルドする」
  • 「Android CIをBitriseに移行して開発者・QAが幸せになったこと」
  • 「Clean Architecture & TDD」

といった、CI、テストツールやテスト手法についてのセッション。 といった、様々な内容のセッションがありました。

当日の資料に関してはこちらにアップロードされているので詳細が気になる方はご覧ください。

f:id:swet-blog:20170926194045j:plain Android Test Night #1 登壇時の様子

Application Build with Docker

ここからは私が発表した「Android SDK with Docker」に話を移したいと思います。

あるアプリケーションをCIに載せようとした際、CIでアプリケーションのビルドを可能にする必要があります。

CI毎にビルドに関する設定方法や環境構築はまちまちで、複数のCIを利用する際はそれぞれのCIに対してビルド環境やビルドフローをメンテナンスする必要が出てきます。
近年のサーバーサイドアプリケーションはビルド環境や動作環境をDockerで統一化を図ることが主流となってきていますが、クライアントサイドアプリケーションではそういったアプローチに関する情報が少ない状態だと思います。

そこで今回はクライアントサイドでもDockerによるビルド環境の統一化ができないか検証してみました。

Android SDK with Docker

今回はAndroidアプリケーションに必要なビルド環境をコンテナイメージ化したAndroid SDK imageと、 そのAndroid SDK imageを元にアプリケーションコードを含めてビルドするApplication imageに分けたいと思います。 Android SDK imageを元にアプリケーションコードをmountしてビルドする手段もあります。 しかし、今回はgradleのキャッシュをイメージに含めたいので、Application imageを作る方向にしました。

Dockerのイメージレイヤーは以下の通りです。

alpine image
|
|-- Android SDK image
    |
    |-- Application image

Android SDK image

まずはAndroid SDKを利用できるDockerイメージが必要です。

ここで注意が必要なのはGoogleのAndroid SDK利用規約 (3.4)により、バックアップ目的以外でのコピー及び再配布が禁止とされている点です。 (gfxさんのこちらのブログ記事に詳細が言及されています)

つまり、Androidをビルドする為のDocker imageは自身でビルドする必要があるということです。
しかしながら、Dockerfile自体にAndroidSDKのライセンスは含まれません。 よって、Github上に公開されているものを利用するか自身で記述することでDockerfileを用意し、自身でDocker imageのビルドを行なってください。

Application image

こちらはAndroidアプリケーションと同じディレクトリ内にDockerfileを作成します。 ADD . /projectでアプリケーションコードをイメージに追加する前にgradleのキャッシュを生成しています。 これはDocker imageをビルドする際、命令によって変更が加わるまではDocker imageキャッシュが利用されるという仕様を利用し、最大限にDockerイメージのキャッシュを生かしています。
仮にADD . /projectの後にgradleのキャッシュを生成する処理をしてしまうと、 ADDで追加するアプリケーションコードに変更が加わると、それ以降の処理はDockerイメージのキャッシュが利用されなくなります。

FROM ${ANDROID_SDK_IMAGE}

RUN mkdir -p /tmp/gradle/app
COPY gradlew /tmp/gradle/gradlew
COPY gradle /tmp/gradle/gradle
COPY build.gradle /tmp/gradle/build.gradle
COPY gradle.properties /tmp/gradle/gradle.properties
COPY settings.gradle /tmp/gradle/settings.gradle
COPY app/build.gradle /tmp/gradle/app/build.gradle
COPY app/mastiff.gradle /tmp/gradle/app/mastiff.gradle

WORKDIR /tmp/gradle
RUN ./gradlew

ADD . /project
WORKDIR /project

RUN echo "sdk.dir=$ANDROID_HOME" > local.properties

CMD ["./gradlew", "tasks"]

この状態で、

docker build -t application:$(git rev-parse HEAD) .
docker run --rm -it \
           -v $(pwd)/app/build:/project/app/build \
           application:$(git rev-parse HEAD) \
           ./gradlew build

と実行することで、アプリケーションをビルドすることが可能です。

登壇したセッションの資料にはAndroidエミュレータをDockerで起動しテストを行う方法も記載しております。しかし、エミュレータでのテストはパフォーマンスがよくないので、デバイスファーム等を利用すると良いと思います。

最後に

今回はクライアントサイドアプリケーションのDockerizeに関してご紹介しました。機会があればサーバーサイドアプリケーションのDockerizeについてもご紹介できればと思っております。

Android Test Night #2 の開催時期は未定ですが、2, 3ヶ月に1回程度開催を予定しておりますので、ご興味がある方は是非お待ちしております。

本ブログでは定期的にこういったテストにまつわる情報をアウトプットをしていきますので、是非本ブログをウォッチして頂ければと思います。

参考文献

iOSDC2017にSWETから3名登壇してきました

はじめまして、1エントリ目を書くことになったSWETの平田(@tarappo)です。

DeNAのSWETグループでブログを始めることになりました。 今後共よろしくお願いします。

SWETってなに?って方もおられるかと思いますが、それについては以下の資料をチェックしてもらえると嬉しいです。

DeNAの取り組むテストエンジニアリング

1発目のエントリーですが、SWETのメンバーが最近登壇した内容について紹介しようと思います。 つい先日行われたiOS開発者のお祭りであるiOSDC 2017(9/15-9/17開催)にSWETから3名が登壇してきました。

今回は、SWETが得意とする領域である「自動テスト」と「CI」、そしてそれに密に関係する「デバイスファーム」といった3つの領域について発表しました。

その3つの発表について紹介したいと思います。

5分枠での発表ですが、その内容はボリュームがずっしりとありますので、是非発表資料と併せて見て頂ければと思います。

f:id:swet-blog:20171024140928j:plain

iOSで利用できるデバイスファームのメリット・デメリットの紹介

モバイルデバイスをクラウドやイントラネットワーク内で一元管理できるデバイスファームは、iOSにおいても、AWS Device Farmをはじめ簡単に利用できるものが増えてきています。しかし、実際に利用したという話は多くないのが現状です。

この発表ではAWS Device Farm、Xamarin Test Cloud、 Testdroid、TestObjectについて 実際に利用した感想を紹介しています。

UIテストの実行時間の短縮に挑戦する

XCTestが進化するにつれ、iOSアプリ開発においても徐々にUIテストが利用されるようになってきました。しかし、UIテスト(UI testing)はユニットテストに比べて実行に時間がかかるという課題があります。

本発表では「複数シミュレータを同時起動してテストを並列実行するOSSであるBluepill」と「物理的に複数台のMacを利用したテストの並列実行」の2つを試した結果、通常の並列化しないテスト実行に比べて、どれだけ実行時間を短縮することが出来たかを発表しました。

2017年のiOSアプリ開発におけるCI事情

iOSアプリ開発においてもCIを利用するのが一般的になりつつあります。 本発表では、そもそもCIとはなにか?、そして世の中にあるiOSアプリ開発向けのCIサービスの比較を発表しました。

おわりに

SWETではiOSDCでの登壇に限らず、積極的に対外的にアウトプットをおこなっています。 また、SWET主催の勉強会(iOS / Android Test Night)も随時開催中ですので、興味がある方は来て頂ければと思います。

本ブログでは定期的に情報をアウトプットをしていきますので、是非本ブログをウォッチして頂ければと思います。