初期化時に束縛します。Y(),});z.doSomething();ご覧の通り、DIをコンストラクタ引数への指定だけで実現しています(この方法はコンストラクタ注入と呼ばれます)。単純な仕組みであることがよくわかると思います。静的型検査のある言語でもどのように表現されるのか見てみましょう。これらは依存先コンポーネントです。後述のコンポーネント内で使われます。これは依存元コンポーネントです。Yへの依存をもっています。初期化時に束縛します。Y()));z.doSomething();静的型検査のある言語でも同様の書き方で実現できることがわかります。して私はこのようなバニラDIを好むのでしょうか。悪い設計だとすぐに破綻するこのうち上の5つは自明だと思いますが、私が特に強調したいのは最後の「悪い設計だとすぐに破綻する」ということです。破綻のスメルが出ている具体例を見てみましょう。バニラDIが可視化する設計の破綻まず、コンストラクタの引数が多くなってきたケースです。}}クラス定義もだいぶつらそうですが、それよりつらいのはコンストラクタの呼び出し側です。))特にSomethingGreatServiceを対象としたユニットテストでは繰り返しこのクラスを作成することになることが多く、テストを書いている途中でうんざりすることになります5。これに対して、DIライブラリを使えば呼び出し側の記述をかなり省略できます。この点だけを見れば、DIライブラリの方が優れているという印象を受けることでしょう。バニラDIでは異なる見方をします。バニラDIでは、このような状況を設計破綻のスメルとみなします。依存対象の関係を整理してこれらをまとめた新コンポーネントを作成さて、これらの解決策によって先ほどの引数が多いという問題も同時に解決できます。前者の責務分割では、SomethingGreatServiceが複数のコンポーネントへ別れますから、それぞれのコンポーネントへコンストラクタ引数が分散されます。後者の依存物の整理では、直接の依存が新コンポーネントを通しての間接的な依存へ変わりますから、直接の依存であるコンストラクタの引数は少なくなります6。このように、バニラDIでは設計の破綻が書きづらさや面倒さとなってすぐに可視化されます。これらを取捨選択して解決していく過程で、自然と適切な責務の分割や抽象化へと導かれるのです。これこそがバニラDIの最大の利点の1つだと私は考えています。終わりにこの記事では、私から見てSWETに入って驚いたこと、そしてテストに関するエキスパートが集結しているSWETでなければ思いもつかなかったバニラDI/Mock宣言について紹介しました。バニラDI/Mock宣言についてご質問やご意見等ある場合は、お気軽にissueかKuniwakまでお問い合わせください。Nightの運営スタッフとしてお会いできることを楽しみにしております。Mockライブラリとは、テストケースの実行に必要な偽のコンポーネントをつくりやすくするライブラリです。JavaScriptではSinon.JS、SwiftではCuckoo、Pythonではunittest.mockが有名です。Injectionをやりやすくするライブラリです。JavaScriptではInversifyJS、SwiftではDIKitが有名です。↩t-wadaさんと家永さんの対談記事:「希薄化したTDD、プロダクトの成長のために必要なものは?〜『健全なビジネスの継続的成長のためには健全なコードが必要だ』対談(6)」↩これらはすべてテスト用の偽のオブジェクトのことです。Stubとは、テスト対象のコンポーネントへの入力をテスト用の入力へ差し替えるためのオブジェクトです。Spyはテスト対象のコンポーネントからの出力を記録するためのオブジェクトです。Fakeはテスト対象からの依存対象を簡易的に代替するオブジェクトです。Patternsを読むことをお勧めします。↩このつらさを解消するためにデフォルト引数を使うこともできますが、一般的にこれはあまりよい選択ではありません。デフォルト引数は何がデフォルトとして使われても問題のないケースでのみ使うべきだからです。デフォルト引数を変えたら壊れてしまうような場合は、定義元を読まない限りわからない暗黙の期待を埋め込んでいるということを意味します。このような暗黙の期待は可読性を損ないます。呼び出し側が特定の振る舞いを期待する場合は明示的に引数に指定する方がよいのです。今回のケースでは何がデフォルトとして使われても構わないとはいえないので、デフォルト引数を使わない方がよいでしょう。素直に考えると、新コンポーネントを作成しても依存している数には変わりないと思うかもしれません。実際はそうなりません。抽象コンポーネントを定義することでStub/Spy等のテストダブルを書きやすくなります。テストダブルは依存物を必要としないですから、テスト内では間接的な依存を意識しなくてすむようになります。テスト外の場合でも、SomethingGreatServiceを内部的に利用するクラスが既に作成済みの依存物を保持していることが多く、テスト内と同様に間接的な依存は見えなくなります。間接的な依存が見えてしまう場合はカプセル化が不十分であることを示唆しています。16:51SWETの新メンバーから見て驚いたこと、そこから生まれたDIライブラリ不使用宣言Tweet2018-04-27Puppeteerによるフルページスクリーンショットを画像遅延読み込みに対応させるブラウザテストこんにちは、薦田です(@toshiya_komoda)。今回はChromeブラウザのブラウザテスト自動化ツールであるPuppeteerに関するTipsを紹介します。DevToolsチームがメンテナンスしています。その主な用途の1つにブラウザ自動テストが挙げられます。PuppeteerはChromeの操作に特化しており、操作対象がChromeに限定されるもののSeleniumにはない便利なAPIを利用できます。Puppeteerで利用できる便利なAPIの1つに、フルページスクリーンショットがあります。フルページスクリーンショットとは、ウェブページの上端から下端までを1つなぎの画像としてスクリーンショットを撮ったものです。以下の画像はフルページスクリーンショットの例です。Blogの過去の記事です。Blog記事のフルページスクリーンショットSeleniumでもスクリーンショットを取得可能ですが、取得対象の領域がviewportに制限されます。このためSeleniumでフルページスクリーンショットを実現しようとすると、スクロールしながら複数枚のスクリーンショットを撮った後、1枚につなぎ合わせるといった実装が必要になります。Puppeteerを利用した場合の実装は以下のように簡潔なものになります。1.3.0で動作確認しています。browser.close()})();補足:Puppeteerの以前のバージョンではフルページスクリーンショットの高さに16384pxの制限がありましたが、1.2.0以降のバージョンではこちらの制限はなくなっているようです。Testなどに利用することが可能です。画像の遅延読み込みに対応したウェブページでは、フルページスクリーンショットをうまく取得できないことがあります。こちらの画像がそのような例です。lazysizesという画像遅延読み込みライブラリのデモページで別に画像を用意して試しています。2.0ページ中にロードしきれていない画像が存在しており、正しくフルページスクリーンショット画像が撮れていません。RequestとしてPuppeteer側での対応が提案されている状況です。現時点では以下のように一度ページ下端までウィンドウをスクロールさせることで問題を回避できます。Screenshotsの実装を参考にしつつ、以下2点の改良を加えたものです。画像ロード以外のネットワーク接続の影響でこの条件が恒久的に満たされないため、timeoutオプション(デフォルトは30sec)を明示的に指定しています。scrollToBottom()関数の中ではwhileループの中でスクロールしながら毎回scrollHeightの値を更新しています。これは、スクロール操作によってページのscrollHeightの値が動的に変わる場合があるためです。ページを一度下端までスクロールしておくことで、さきほどのページのフルページスクリーンショットは以下のように改善されます。遅延読み込みページのフルページスクリーンショット(遅延読み込み対応版)画像が全てロードされ、きれいなフルページスクリーンショットが取得されています。補足Tips以前のバージョンにあった高さ制限の問題とは別に、Puppeteerでは非常に大きなフルページスクリーンショット画像を取得しようとすると、その下端がかけてしまうことがあります。こちらの現象は遅延読み込みとは別の問題ですが、こちらのissueで説明されています。こちらの問題はChromium側での対応が必要ということで、Chromiumの不具合としてあがっていますが、Puppeteer1.3.0にバンドルされたChromiumでは未対応です。この現象が発生した場合には、スクロールしながらスクリーンショットを複数枚取得し最後につなぎ合わせる、といったSeleniumの場合と同様のワークアラウンドが必要になります。2018でSWETの取り組みについて発表してきた発表・イベントリポート初めまして、SWETの坂本です。2018のBlueStageにて、SWETの取り組み内容についての発表を行いました。発表資料は以下になります。今回の発表ではSWETの具体的な取り組みとして、"画像"が絡むようなテスティングに関する2つの取り組みについて、前後半に分けて紹介しました。Testを支えるクローラーについてお話ししました。Regressionに使うためのスクリーンショットの取り方のテクニックなどについて述べました。単純なクローラでは突破することができない登録フォームなどに対応するために、機械学習の活用を検討している状況についても発表しました。後半の発表では、テンプレートマッチング(画像処理)を使った自動化のツラミを低減するためにSWETで開発しているサービス「ExMachina」についてお話ししました。ExMachinaの基本的なアイデアは、「ページ情報」という形でテスト用のイメージ素材を管理することにより、WebやNativeアプリと同様にセマンティックな情報を利用してUIテストの自動化が行えることです。発表ではその基本的なコンセプトを共有し、ExMachinaを使った自動化の処理の流れ、ExMachinaのクローラーへの応用などについて説明しました。おわりにSWETでは、まだベストな方法が確立されていないテスティングの領域に対してR&D的な取り組みを行っており、今後も拡充していきたいと思っています。その取り組みの中で機械学習を活用したアプローチも検討しています。発表・イベントリポートはじめまして。SWETの外山(@sumio_tym)です。2018の参加レポートをお届けします。DroidKaigi実行委員会このエントリでは、SWETらしく、テスティングに関係するセッションを以下の2つに分けてご紹介します。SWETメンバーによる発表Androidで利用できるデバイスファームのメリット・デメリットの紹介duckさんによる発表です。以下の観点で比較していました。端末を操作できるか?自動テストで何が動かせるか?利用者がテストを用意しなくても良い「モンキーテスト」ができるか?adbで接続できる「ダイレクトアクセス」ができるか?CI/CDと連携可能か?また、最後に、以下の点を強調していたのが印象的でした。「必ず、無料枠で、自分達の業務に適用できるか試してみること」デバイスファーム選びの参考にしてみてください。UIテストの実行時間を短縮させる方法tarappoさんによる発表です。Hirata前半では、UIの自動テストに関する以下の点について、具体例を挙げながら解説していました。後半では、実行時間短縮のために必要な、以下のプラクティスを解説していました。ボトルネックを特定しつつ更に並列度を上げていく過程はとても参考になると思います。Espressoテストコードの同期処理を究める外山の発表です。テスト対象アプリのUI更新の完了を待てずに失敗することがあります。Automatorを併用してUI更新完了を待ち合わせる方法について説明しました。独自の非同期処理機構を持つRxJavaなどを採用しているアプリでは、Espressoによるテストの失敗頻度が高くなります。そのようなときに、この発表を参考にして、失敗しないEspressoテストに書き換えてみてください。外山が過去のDroidKaigiで発表した以下の内容も役に立つと思います。あわせて参照してみてください。Test@fushiroyamaさんによるハンズオンです。自分のEspressoの発表と時間が重なっていたため、参加は叶わなかったのですが、スライドを読むだけでも参考になります。Testを書くのに必要な知識が、MockitoやRobolectricの使い方も含めて丁寧に解説されています。Testに入門したい方はチャレンジしてみると良いと思います。testskiriminさんによる発表です。MVPアーキテクチャを採用したアプリにおいて、Presenter部分をテストする方法を解説する内容でした。正しくViewとPresenterを分離し、Mockitoを使ってテストを書く方法が丁寧に説明されており、具体的でとても分かりやすい内容でした。Androidのユニットテストを書くときの参考にすると良いと感じました。Schnelleさんによる発表です。JUnit4が抱えている問題の説明から始まり、以下のような流れで解説していました。#1でも少し話題になっていました。Testもサポートしている(Experimental)とのことです。AndroidでJUnit4が使えるようになるまでは長い時間が必要でしたが、JUnit5は案外すぐに使えるようになるかも知れません。2018のセッションのうち、テスティングに関係するものをご紹介しました。SWETグループでは、DroidKaigiでの登壇に限らず、積極的に対外的なアウトプットを行っています。Nightも開催していますので、ご興味の有る方は是非ご参加ください。SWETグループではAndroidに限らずテストの自動化やCI/CDに興味があり、私達と一緒に働いてくれる仲間を募集しています。testingことはじめ(3)〜アプリケーションのテスト〜Golangこんにちは。パッケージを使ったユニットテストについてお伝えしてきました。httptest)今回はGolangで作成したアプリケーションをテストする際に利用できるライブラリなどについて紹介します。ありますので、実際に動作させることが可能です。依存パッケージのダウンロードなお、文章中のコマンドは全てバージョン1.9.2での動作を確認しています。アプリケーションのテストアプリケーションをテストする場合はいくらかのテクニックが必要になります。ここでは、モックを使ったテストやウェブアプリケーションのrouting(エンドポイント)のテストについてGolangでの書き方について記述します。モック通常、ある程度の大きさ以上のアプリケーションを作成する場合、複数のパッケージに分けて分割します。分割したアプリケーションのそれぞれのコードをテストする場合、モックを利用して外部(テスト対象以外)の依存を減らすことが望ましいです。フレームワークと組み合わせて利用することができます。静的なモック用のソースコードを作成します。インストールできます。Golangはツールの配布・利用も簡単です。github.com/golang/mock/mockgenモックの作成モックはインタフェースに対して作成します。インタフェースが含まれているので、これに対してモックを生成します。apple.cultivar}mockgenコマンドを実行します。-sourceオプションでインタフェースが含まれるソース、-destinationオプションで出力先を指定します。接頭辞をつけたものになります。オプションで変更することができます。確認することができます。引数にした以下の関数に対してテストを記述します。food.Name())}モックインスタンスは以下のように作成します。引数で戻り値を指定することができます。}}上記の例でduck.Eat(food)では、FoodのName()を呼び出します。モックを作成して特定の文字列を返却するように指定しているのでテスト実行時の依存を減らすことができます。例では引数は必要ありませんでした。戻り値を設定したい関数に引数が必要な場合は、呼び出される際の引数を指定して限定できます。string}というインタフェースを想定します。モックを利用する場合は以下のようになります。hogeMock.EXPECT().Foo(gomock.Any()).Return("bar")フラグのテストコマンドライン引数やオプションなど、クライアントツールやサーバーアプリを作成した場合にフラグを利用することはよくあると思います。フラグによって動作を変更する場合のテストを想定します。とエラーになってしまいます。そこで、フラグを設定する場合flag.Parse()は利用せずflag.NewFlagSet()で生成されたインスタンスを利用することで、テスタブルなコードを記述することができます。フラグをパースする関数は以下のようにしました。*optC}上記の関数は以下のように利用することができます。optC)}関数ParseFlagをテストするコードは以下のようにしました。0.013s異なる値でフラグのパースを複数回行なっていますが、エラーにならず実行できています。Webアプリケーションのroutingをテストするウェブアプリケーションを開発する場合は、実際にエンドポイントへアクセスして正しい結果が返ってくるかテストすることがあります。アプリケーションを起動してテストする利用するフレームワークがhttp.Handlerの実装となっている場合は後述のhttptestが利用できるので、そちらを利用した方がよいです。ここでは、実際にアプリを起動してhttpクライアントからリクエストを投げ、ハンドラーの想定通りのメソッドが実行されているかどうかを確認します。このサンプルではEchoフレームワークを利用しています。ランダムで空いてるポートをListen多くのウェブフレームワークではサーバー起動時にポートを指定しますが、指定したポートが利用されていた場合はテストが実行できません。以下の関数はランダムで空いてるポートを取得します。以下のソースコードについてのテストを想定します。実際にサーバーを起動し、正しいレスポンスが返却されるかをチェックします。message)}利用するウェブフレームワークがフォアグラウンドでサーバーを起動する場合、テストケース内でサーバーを起動すると、そこでテストがストップしてしまいます。別のターミナルセッションでサーバーを起動した後でテストを実行することもできますがテスト実行以外のプロセスに依存するのは避けた方がよいでしょう。goルーチン内でサーバーを起動することで、テストケース内で完結するようにしてみます。0.021sログからも実際にサーバーが起動しているのがわかります。上述の例では正しく動いているように見えました。サーバーの起動に時間がかかってしまう場合はどうでしょうか。}ウェブフレームワークechoのコードに手を加え、サーバーが起動する際にSleep処理を追加しました。refusedのエラーが出てしまいました。この場合、サーバーが立ち上がるまで待つといった工夫をしなければなりません。サーバーの起動待ちを実装するのは面倒ですが、簡単に実現できる方法が用意されています。ここではgoルーチンを使わない方法でテストを書き直してみましょう。httptestを利用してテストを実行するGolangでは、Webサーバーのテストをサポートするnet/http/httptestも用意されています。このパッケージを利用することで、routingのテストも容易になります。シンプルに記述することができます。0.013shttptest.NewServerすることでラップされたサーバーが立ち上がります。ウェブフレームワークのStart()をコールしているわけではないので、Sleepされません。ポートも自分で指定せずにランダムで空いているポートを利用してくれます。リクエストをシミュレーションするhttptestではサーバーを起動するほか、リクエストをシミュレーションする方法を用意しています。レスポンスはResponseRecorderを介して行われます。Recorderを利用することでレスポンス内容の取得も楽になります。レコーダーのBodyはio.Readerにバッファ機能をつけたリッチなbytes.Budder型となっています。そのためString()で変換することができ、テストもシンプルに記述できました。0.011s最後にこれまで3回に渡ってGolangが標準で提供しているテストの機能の一部を紹介しました。言語としてテストを強くサポートしていることが感じられたのではないでしょうか。紹介しきれていない機能やオプションなどがたくさんあります。今後も有益な機能について紹介できればと思っております。testingことはじめ(3)〜アプリケーションのテスト〜Tweet2018-01-22Golangでtestingことはじめ(2)〜テストにおける共通処理〜Golangこんにちは。前回からGolangのテストについて紹介をしています。今回の記事はその2回目(テストにおける共通処理)に当たります。ありますので、実際に動作させることが可能です。依存パッケージのダウンロードなお、文章中のコマンドは全てバージョン1.9.2での動作を確認しています。テストの共通処理いくつかのテストケースを書いていて、共通処理をまとめたいと思ったことはないでしょうか。他言語のテスティングフレームワークには、必ずといっていいほどこの機能が提供されています。*testing.M)を利用します。TestMainのみ実行します。呼ぶことで各テストケースが実行され、失敗か成功かに応じてコードを返却します。渡ればそのテストファイルは成功、それ以外の値の場合は失敗になります。前後に処理を挟むことでBeforeAllとAfterAllが実現できます。AfterEach続いてテストケースごとに前処理や後処理を実行する方法です。ループによるBeforeEachの実現この方法は可読性も低く、テストケースを指定した場合無駄に処理が行われてしまうので推奨しません。BeforeEachを実現しています。初期化しようとしています。コード全体を見るとケース名と関数をスライスにいれ、それをループしてサブテストとして実行させています。0.007sテストケースの前後に処理を挟むことができました。オプションで特定のテストケースのみを実行することができます。ある関数のリファクタリングをする場合など早いフィードバックが欲しい時は、テストケースを指定することで実行時間の短縮が期待できます。余分にプリントされてしまっています。オプションでは上から順にテストを実行し、一致しないものをスキップするようです。スキップしたテストケースについても前処理を実行してしまうため、前処理が重い場合は全体の実行時間が長くなってしまいます。前処理に5秒のSleepを追加してみます。10.014sSleepを追加した結果、全体の実行時間が10秒もかかってしまいました。これでは、テストケースを指定しても実行時間の短縮になりません。前後処理とテストケース内の処理が分離されてしまいコード全体の可読性も悪くなってしまっています。上述の例では、テストケースの外で共通処理を呼び出すとテストケースを指定した場合は無駄に共通処理が行われてしまいました。各テストケース内で共通化した関数を明示的に呼び出すよう書き換えてみましょう。duck}可読性は非常に高くなりました。5.011s実行時間も前処理1回分になりました。共通メソッド内でエラーが発生した場合、テストを失敗させる必要があります。インタフェースを渡してやります。コールすることでテストを失敗にし、その場でそのテストケースを終了させてやることができます。場合はテストを失敗にしますが、テストケースは引き続き実行されます。2つのサブテストにおいて同一箇所で失敗したと表示されてしまいました。1つのケース内で複数回コールされる関数の場合、どこで失敗したか特定しにくくなってしまいます。これを、ケース内のどこで失敗したかわかるようにしてみます。呼び出し部分が表示されるようになります。先ほど変更したソースコードに、さらに変更を加えます。10.013s表示されるソースコードの行数が変わっていることがわかります。こちらの方がテストケース内のどこで失敗したか原因を探りやすくなると思います。最後にテストケース毎に実行する前処理や後処理は、Golangでは少し工夫が必要でした。シンプルに記述することができ処理の流れが分かりやすいようになっていると思います。次回はアプリケーションやツールのテストで利用できる機能(gomockとhttptest)について紹介します。(1)リンクプライバシーポリシースターをつけました引用をストックしましたストック一覧を見る閉じる引用するにはまずログインしてくださいログイン閉じる引用をストックできませんでした。再度お試しください閉じる限定公開記事のため引用できません。