Selenium でキーワード駆動テストを実践したときのあれこれ
この度「第4回 日本Seleniumユーザーコミュニティ勉強会」で発表をさせて頂くことになりました。
日本Seleniumユーザーコミュニティの皆様、貴重な機会を頂きありがとうございます。
振り返れば10ヶ月
右も左も分からない状況ながら、今年2月3日に開催された第3回の勉強会に運良く参加をさせて頂くことが出来ました。
その時感じた日本Seleniumユーザーコミュニティの皆さんの温かさ、知識、そして私自身の興味を追い風に、約10ヶ月の間様々な方に助言を頂きながら次のことに集中して取り組むことができました。
- テスト
- キーワード駆動アプローチ
- システム開発手法
10ヶ月の成果
お陰様でこの10ヶ月間で取り組んだ成果を皆さんにお伝えする準備が整いました。
まだまだ足りない部分はありますが、この機会を利用させて頂いて一区切り付け、また次に向けて残る課題や新しい事にに取り組んでいきたいと思います。
今後も皆さんから力を借りることが多々あるとは思いますが、逆に私もこのような場やWebを通してお役に立てればと思います。
今後ともお付き合いの程、よろしくお願いいたします。
Selenium Serverをサービス化した時の話
はじめに
Selenium関係の情報をネットで検索すると「Seleniumを使ってー」、「Seleniumによるー」等といったキーワードをよく見かけます。
今回の記事では私も最初は次のようなタイトルを考えていました。
「Selenium をバックグラウンドサービスとして動かす方法」
"Selenium"と一口に言うけれど
今回のスライドを作る中で、W3C WebDriverのドキュメントを読んだり、
上記の記事を読むうちに、自分が言う"Selenium"って何を指すんだ?と疑問に思うようになりました。
本スライドの"Selenium"
本スライドでは"Selenium"という曖昧な表現はなるべく避け、"Selenium Server"という表現を用いました。
これは、"Selenium"が「プログラミング用のライブラリである"selenium-java"等」のことを指していたり、「WebDriverに定義されている"JSON Wire Protocol"に基づくHTTP通信の送受信をサポートするサービス」を指していたりする場合があるためです。
"Selenium"というキーワードに関して、この2点を抑えておけば混乱しないと思います。
Selenium Serverをサービス化した時の話
以上の前提を踏まえて、ご覧いただければと思います。
皆さんの自動テスト実行環境構築に役立てて頂ければと思います。
また、今回のスライドが便利な機能を提供してくれているJava Service Wrapperの今後の継続開発に何らかの形で貢献できればとも思います。
こちら会社に所属していたり、利害関係があるわけでもないのですが、純粋に大変便利なツールだと思うので多くの人に使われ続け、私も使い続けることができればいいと思っています(*^_^*)
スライド形式なので、説明不足な点があるかもしれませんが、何か気になる点、ご質問があればコメント頂けるとうれしいです。
テストを自動化したい人のための、テストランナーの選び方
はじめに
『自動テスト』というと、テストを自動で上手いことやってくれる印象を受ける方もいるかもしれません。
というのも、Selenium を知った頃の私がそんな感じだったからです。
当時の私はテストに苦手意識を持っていたので、「Selenium という夢のようなアプリケーションがあるらしい!」ーと勝手に思っていましたσ(^_^;)
自動テストシステム開発解説文献の有り難み
JUnitやTestNGといったテストランナーを実装したツールを知らない初心者の方が純粋なSelenium 本を最初に読むと、「たくさんあるテストケースをどうやって実行するのだろう?」「ループ文で繰り返すのかな?」なんて疑問に思うでしょう。(私だけかな?(^_^;))
はじめて間もないころにツールを使ったテストの実行方法を紹介した文献や実践してる人に出会えると良いのですが、そう上手くはいかないこともあるでしょう。
テストランナーの選び方
そこで、今回は私の経験を交えてその辺について整理しましたので、同じような悩みを抱える方は参考にして頂ければと思います。
やや物足りなさがあるため、今後追記するために余白を残しました。
JUnitやTestNG以外にも比較したり、TestNGのコードを掘り下げてみたりすることがあると思いますのでご容赦ください。m(_ _)m
Selenium でキーワード駆動テストを実践したときのあれこれ 『まずはやってみた編』
はじめに
12/18 Selenium 勉強会4で予定しているセッションの冒頭の話をします。
勉強会では、本編で浮き彫りになった改善点に取り組んだ話を、『リファクタリング編』として銘打ち、掘り下げて話す予定です。
それぞれ状況が異なれば、様々な見解があると思います。
セッションとうまく話が繋がればセッション内で言及したり、合わなくても後日お話をさせて頂きたいと思いますので、よろしければコメントください。
Selenium でキーワード駆動テストを実践したときのあれこれ まずはやってみた編
リファクタリング編との繋がりを考慮し、スライドによる投稿で失礼します。
※ 駆け足で資料を作成した為、後で手直しするかもしれないです。
すみません m(_ _)m
まとめ
最後のスライドの繰り返しにはなりますが、 はじめてみるとひとりの取り組みでさえも、様々な要求が湧いてきます。
これらをクリアする為に取り組んだ内容を、別日のアドベントカレンダーや勉強会4での『リファクタリング編』で紹介しますので、しばらくお待ちください(^ ^)
ユースケース駆動開発についての勉強まとめ そして思ったこと
先日、以下の書籍を使ってユースケース駆動開発について勉強をしました。
- 作者: ダグ・ローゼンバーグ,マット・ステファン
- 出版社/メーカー: 翔泳社
- 発売日: 2016/01/28
- メディア: Kindle版
- この商品を含むブログ (1件) を見る
経緯
私はキーワード駆動テストアプリケーションを作っています。
せっかく時間をかけたものなので、これを公開してもっと多くの人に利用してもらおうと思いました。
ただ、そこにはまだまだ実現したいことが多く残っていました。
そこで、私はアプリケーションを利用してくれる方の中から開発に参加してくれる方を募ると良いのではないかと考えました。
しかし、私は一つの課題があることに気づきました。
これがどういうものなのかが、私にしか理解できないものになっていたことに。
そこで、対象の構造を改めて誰もが分かる形で表現するにはどうしたらいいのか?と考え、
手に取ったのが本書でした。
本書を読んだのは、本当にたまたまです。
他にもUPやRUPの本、DDDやRDDの本も手に取りましたが、要求分析からテストまでの開発の流れを体系的に理解する必要がある今の私には、これが一番自分に合っていたからです。
ユースケース駆動開発において予備設計に進める上で重要だと感じた点
要求
機能要求…システムができること(非機能も含める)
振る舞い要求…機能要求からユーザーとシステムの対話を明らかにしていくこと。
勉強のまとめ
私はJJUGに参加しているのですが、昨年参加した「ビール片手にLT大会」が今年は11/21に開催されるということでした。
ちょうど良い機会だと思い、勉強のアウトプットもかねて発表してきたので、備忘録として残します。
様々な開発手法
ロバストネス分析の結果を明らかになった、バウンダリ/エンティティオブジェクト、コントローラからシーケンス図を作成し、
そこに表現されたオブジェクト間の相互作用、必要な属性をオブジェクトに反映し、抽象的に表現した図がクラス図になります。
ユースケース駆動開発であるICONIXは、このようなアプローチで開発を進めていく手法を採用しています。
一方、本書を読む中で「そもそもオブジェクトととは?」について調べる為に参照した下記書籍では、ユースケース駆動開発とはまた別のアプローチでオブジェクトをに他の手法が紹介されていました。
詳しくは別の機会にしますが、OOADというジャンルではオブジェクトを設計をしていくという目的は同じなのですが、そこへのアプローチの仕方は他にも様々あるのだなと感じました。
私が大切だと思ったこと
開発の話からそれますが本書だけでなく、日常生活からの体験も踏まえて思ったことがあります。
- 自分が何を大切にしていて、何かをするときに関わる人が居るときはその人は何を大切にしているのかを話してお互いに納得すること
- 様々なシーンでの役割を理解すること
- 良い仕事は良好な関係から生まれること
オブジェクト設計ではないですが、この世で生活をする上ではいろんなシーンがありそこでには自分の役割があるはずです。
- 職場での役割
- 家庭での役割
- 社会での役割
役割がなければ自分で作ればいいのかなと思います。
役割が合わないと思えば、合わせるか、変えていけばいいのです。
私はオブジェクト設計を通してこんなことを思いました。
変な人ですね(^_^;)
今後、UPやRUP、DDDのアプローチついて勉強するのが楽しみです。
デザインパターン学んで得られたこと
キーワード駆動アプローチのテストシステムを作る上でその構造について悩んだ時に読んだこちらの書籍と、そこから学んだことを受けて考えを巡らせた時のことについて書きます。
- 作者: 結城浩
- 出版社/メーカー: ソフトバンククリエイティブ
- 発売日: 2004/06/19
- メディア: 大型本
- 購入: 51人 クリック: 762回
- この商品を含むブログ (399件) を見る
デザインパターンの書籍を読んだ感想
テストシステムを構造化、アイディアを形に(実装)する上での良い参考例になりました。
特に「保守性」や「分かり易さ」が鍵になると考えていた次の要件を実現することができるようになったと思います。
- 継続的にメンテナンスしながら使用していきたい
- 他の人にもメンテナンスしてもらいながら利用してもらいたい
デザインパターンから具体的に恩恵を受けた点
具体的には次のような点でデザインパターンの恩恵を受けることができました
- 自分のプログラムを構造化できた
- 再利用しやすい構造にリファクタリングできた
- JUnitといったOSSのソースを読めるようになった(コードの意図を理解できるようになった)
- アプリケーション全体を設計する手法を知らないことにも気づけた
おかげで、OOADについて知り、ある方の紹介でユースケース駆動設計についても知る機会を得ることが出来ました。
jugemix.hatenablog.com
「ICONIX」では、デザインパターンは詳細設計フェーズでオブジェクト間の相互作用を構造化する一つの手法として紹介されています。
設計について勉強しようと思っている方は、デザパタの勉強から始められると、プログラムの構造設計からアプリケーションの構造設計へと思考を広げられるようになるので良いと思います。
実際に役立ったデザインパターン
ここからは実際にOSSの中で見られたデザインパターンや、テストシステムを構築する際に実際に役立ったパターンを具体例と合わせてまとめます。
チェーンオブレスポンシビリティ
目的の処理を持つオブジェクトをチェーンのように実行していく。JUnit4.x系ではStatementクラスとして採用されているパターンである。
次のソースのmethodBlockメソッド(281行目参照)を参照いただきたい。
github.com
ここでは、Statement抽象クラス型のstatementオブジェクトに実行するオブジェクトRunBefores/RunAfters等のオブジェクトが追加されていることに気づくと思う。
テンプレートメソッド
抽象メソッドで構成する処理の流れを具象メソッドにまとめ、抽象メソッドの実装はサブクラスに任せる。
これにより、具象メソッドで処理の全体の流れは強制しつつ、具体的な処理内容は個別の実装に任せたい時に有用。
共通の前処理と後処理の間に個別の処理を実装したいーといった時に効果を発揮する。
例えば、チェーンオブレスポンシビリティで紹介したBlockJUnit4ClassRunnerクラスは実際にインスタンス化されるランナーであるが、この継承元のParentRunner抽象クラスではこのStatementを実行するrunLeaf具象メソッド(321行目参照)が実装されていて、Statement自体の作りは各具象ランナーに委譲しているテンプレートメソッドパターンとなっていることが読み取れると思う。
github.com
少し脱線しますが、JUnitを使ったテストでテストの実行順や必要な処理を追加するような場合にカスタムランナーを作られると思いますが、その場合は、このStatement型オブジェクトに意図した順に実行されるロールのオブジェクトを追加することが妥当なランナーのカスタマイズ方法だと私は思います。
これを実践しているのが、PitaliumのPtlBlockJUnit4ClassRunnerWithParametersクラスですので、参照されると良いでしょう。
github.com
ファクトリーメソッド
オブジェクトを生成する側のクラスでnewをしないでオブジェクトを生成する。
一般的なプログラミングの入門書では、生成する側のオブジェクトが生成される側のオブジェクトをnewするような記述になっている。
しかしこの場合、生成される側のクラス名等に変更が発生した場合、生成する側のオブジェクト格納先、そのオブジェクトを利用するコードにも影響が発生するがある。
そこで生成する側のオブジェクトで、生成されるオブジェクトのインタフェースを受けとるようにファクトリーメソッドを記述したらどうだろうか。
インタフェースを実装するクラスに変更があっても生成する側には、生成する箇所にしか影響が及ばないようになる。
インタフェースを介しているので、インタフェースオブジェクトを利用するコードには影響が及ばないのである。
このようなことからファクトリーメソッドを利用すると、生成されるオブジェクトと、オブジェクトを生成する側のオブジェクトの結合度を緩やかにする効果がある。
WebDriverのPageFactory.initElementsはページオブジェクトを生成する際の良い例である。
github.com
ブリッジパターン
機能のクラス階層が実装のクラス階層のインスタンスを保持し、役割を保持する構造のパターン。
機能の追加、実装の追加を同時に行うことができる。
コマンドパターン
処理をオブジェクト化することで、その処理のメンテナンス性と拡張性の両方を兼ね備えることができる。
その他役立ちそうなデザインパターン
その他にも役立ちそうだと思ったパターンを簡単にまとめます。
イテレータ
対象データをコレクションとして扱い拡張for文を使うことで、そのコレクションに格納しているデータを繰り返し操作できるようになる。
データを保管するクラスを作る時に、イテレータを実装するとデータの取り扱いが容易になることがある。
シングルトン
インスタンスを複数作らず、使い回したいときに有効。リソースを節約できる。
PitaliumのWebDriverはWebDriverManagerシングルトンクラスにより、テスト間で使い回し可能な作りになっている。
ステート
状態を具象クラスとして扱う。そのクラスが状態に関する共通の処理をインタフェースとして実装する。
この結果、状態に応じて処理を呼び出す側は、状態インタフェースを実装したクラスのオブジェクトを保持することで、現在の状態を気にする必要がなくなる。
まとめ
一般的に知られているデザインパターンを知り使うことで、誰にでも分かりやすいコードが書けるようになることが分かったかと思います。
今後、詳細設計を行う際には、このようなパターンの適用も視野に入れて開発に取り組んでいきたいと思います。
JUnit4.13のソースを追っかけてみた
先日参加したSQiP「キーワード駆動テストシステムを作ろう」で学んだ、ランナーの役割をJUnitでも実践できるのかを検証する為に、JUnit4のソースコードを読んでみました。
作ったクラス図
折角、なんちゃってクラス図を書いたので公開します。
参考にしたサイト
最初は「ランナーってなんだ?」というところから入って、こちらのサイトを参考にし始めたのですが、分かったような分からないようなままになっていました。
もやもやしたまま、SQiPのチュートリアルを迎えて、ランナーの役割が分かったところで、JUnitのソースを読むと不思議と頭に入ってきました。
途中、こちらのサイトも参考にさせて頂いたり
ihirokyさんは、super.runChild()をオーバーライドしたrunChild()の中で呼び出しています。これも一つの方法ですよね。
BlockJUnit4ClassRunnerを継承したカスタムランナーなので、必ずBefore/Test/Afterの間に実行されるようになります。
また、こちらのサイトを参考にさせて頂いたりしました。
渡辺さんのカスタムランナーは記事でも書かれているように、ユニットテスト以外のテストにも利用されています。なので、run(notifier)からinvokeTest(notifier, testcase)を実行するような作りでカスタムランナーを作られています。面白いです。
結局何がいいのかというと、JUnitを使ってどんな風に「テストを実行したいか」が重要なのかなと思いました。
渡辺さんも書かれていますが、JUnitは「テストケースがクラス毎にメソッド単位で定義されている」というところが原則です。ですから、この考え方に沿ってJUnitを利用するのが、自然な使い方かなと僕も思います。
僕がカスタムランナーを作るとしたら
自分が作っているキーワード駆動テストシステムで共通した画面操作を共通処理に移すにはこうすると思います。
例えば、毎回ログインログアウトが必要なシステムだとします。
これをいちいちテストに書くのは面倒なので、共通処理化してしまいます。
案1)
・カスタムランナークラスの定義
public Class LoginTestLogoutRunner extends BlockJUnit4ClassRunner
・methodInvokerのオーバーライド
@Override protected Statement methodInvoker(...) {
return new CustomInvokeMethod(method, test);
}
・@Testを実行するInvokeMethodのを継承したカスタムInvokeMethodの定義
public class CustomInvokeMethod extends InvokeMethod
案2)
もしくは、JUnitのStatementクラスが「Chain of Responsibility」パターンを利用している事を利用して、Statementのサブクラスを追加しちゃうのも面白いかもしれませんね。
@Testでは、キーワードスクリプトが処理をして、その前後で共通処理を実装できればよいので。
そうすると、テストクラスはシナリオのパターン分できますが、@Testの前後で共通の処理を固定的に実行することが出来ますね。
案3)
@Ruleとして必ず実行させるような仕掛けにしてしまうのも、ありかもしれません。
9/25追記
案4)
カスタムランナーを作らずに済ませる簡単な方法は、@Testに共通処理を書いてしまって、意図した順番で実行する―というのも、一つの手かと思いました。
これなら1クラス=1テストケースとして取り扱うことが出来ますね。
渡辺さんが仰っているように、JUnitの構造上は「1メソッド=1テストケース」ですから、この点を気にしなければこの方法が一番簡単かと思います。
自動でテストを流す時に、実行するクラスが増えますが、Suiteランナーで実行するクラスをまとめてあげれば問題ないと思いました。
まとめ
いづれにしてもJUnitはまだまだ奥が深いし、これを使ってやりたいことはまだたくさんあります。内容を紐解いて自分がやりたいことを深堀した結果を公開したいと思います。