Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Apache Wicketのユニットテスト機能

764 views

Published on

Wicket-Sapporo 2013-11-21

Published in: Engineering
  • Be the first to comment

Apache Wicketのユニットテスト機能

  1. 1. Wicketのユニットテスト機能 (WicketTester) Wicket-Sapporo 2013-11-21 rev,2 @gishi_yama
  2. 2. Wicketには ページやコンポーネントの ユニットテストを補助する 機能が備わっている
  3. 3. ユニットテスト 詳しくは
  4. 4. ユニットテスト とは "...ユニットテストでは、対象のクラスやメソッドが期待された振る舞 いをするか検証し、テストが成功することによってそれを保証します。 この「期待された振る舞い」とは、言い換えれば、対象のクラスやメソッ ドの仕様です。" (渡辺修司, "JUnit実践入門 体系的に学ぶユニットテストの技法", 技術評論社, 初版, pp.29) "...ユニットテストを繰り返し何度も実行することで、プログラムに問 題が発生したときに、早い段階で影響範囲などをチェックできます。
 ...リスク回避のために避けてきた機能拡張やコードの修正を安心して行 う事ができます。" (渡辺修司, "JUnit実践入門 体系的に学ぶユニットテストの技法", 技術評論社, 初版, pp.30-31)
  5. 5. public  class  Calculator  {          public  int  add(int  x,  int  y)  {          return  x  +  y;      }       } public  class  CalculatorTest  {      private  Calculator  calculator;          @Before      public  void  setUp()  {          calculator  =  new  Calculator();      }          @Test      public  void  addメソッドで0足す1が1になる()  {          int  actual  =  calculator.add(0,  1);          assertThat(actual,  is(1));      }    @Test      public  void  addメソッドで3足す4が7になる()  {          int  actual  =  calculator.add(3,  7);          assertThat(actual,  is(7));      }         } 期待通りの振る舞いをしているか プログラムで自動的に確認
  6. 6. 画像出典 : upload.wikimedia.org WicketTester
  7. 7. WicketTester new  WicketTester(WebApplication); 引数に渡されたWebApplicationのインスタンスをもとに、 プロジェクトがあたかもWebサーバ上で動いている様な テスト用の実行環境とメソッドが提供される。 JUnitテストクラスのフィールド変数に定義しておくと楽。 private  WicketTester  tester;       @Before   public  void  setUp(){      tester  =  new  WicketTester(new  WicketApplication());   } 使用例
  8. 8. 画面表示についての ユニットテスト機能
  9. 9. WicketTester#startPage tester.startPage(WebPage); 引数に渡されたページのテストが開始(ページが表示*)される。 各テストを行う前の準備段階として実行する。 引数は、ページのインスタンスもしくはクラスでもよい。 @Test   public  void  Test1()  {      //  ページのテスト開始(ページの表示)      tester.startPage(new  HomePage());   } 使用例 *ここでの「表示」はもちろん仮想的な意味であって、ブラウザでアクセスできるわけではない
  10. 10. WicketTester #assertRenderedPage tester.assertRenderedPage(Class); 最後に表示されるページのクラスが引数と一致するかがassertされる。 ページが正常に描画されたかどうか、という意味でのテストに加え、 後述するLinkやFormのページ遷移結果のテストにも用いる。 スーパークラスが渡された場合も一致と見なされるので注意。 @Test   public  void  初期状態でページが表示される()  {      //  HomePageのテストの開始(ページの表示)      tester.startPage(new  HomePage());      //  画面に表示されるページが  HomePage.class  のページか      tester.assertRenderedPage(HomePage.class);   } 使用例 assert
  11. 11. WicketTester#assertLabel tester.assertLabel("id",  String); idのラベルに表示されている文字列がStringと一致するか
 がassertされる。 @Test   public  void  ラベルに正しく文字列が表示される(){     tester.startPage(HomePage.class);     //message1に  こんにちは!  が表示されるか     tester.assertLabel("message1",  "こんにちは!");   } 使用例 assert
  12. 12. WicketTester#executeUrl tester.executeUrl("./foo/bar");   引数に渡されたCleanURLのページが表示(テストが開始)される。
 (つまり、startPageのCleanURL版) @Test   public  void  パラメータありのCleanURLでページが表示される()  {      //  URLで表示されるページのテストの開始(ページの表示)      tester.executeUrl("./foo/communityId/123");      //  画面に表示されるページが  FooPage.class  のページか      tester.assertRenderedPage(FooPage.class);      //  communityIdのラベルが1000と表示されるか      tester.assertLabel("communityId",  "123");   } 使用例
  13. 13. WicketTester#clickLink tester.clickLink("id",  boolean); idのLinKコンポーネントのonClickメソッドが実行される。 クリックされた後の結果を、他のassertメソッドでチェックする。 第2引数はAjaxが有効な環境でのクリックかどうか(省略するとtrue) @Test   public  void  BarPageに遷移される()  {      tester.startPage(new  WS01IndexPage());      //  toBarPage  Linkコンポーネントがクリックされる      tester.clickLink("toBarPage");      //  SimplePageクラスのページに遷移されるか      tester.assertRenderedPage(BarPage.class);   } 使用例
  14. 14. WicketTester コンポーネントの状態のassert //  idのコンポーネントが表示状態か   tester.assertVisible("id");   //  idのコンポーネントが非表示か   tester.assertInvisible("id");   //  idのコンポーネントが有効か   tester.assertEnabled("id");   //  idのコンポーネントが無効か   tester.assertDisabled("id");   //  idのコンポーネントが入力必須か   tester.assertRequired("id") それぞれ、idのコンポーネントの状態がassertされる。 assert
  15. 15. WicketTester #assertComponentOnAjaxResponse tester.assertComponentOnAjaxResponse("id"); idのコンポーネントがAjaxで更新(AjaxRequestTarget#addに追 加)されたかがassertされる。 @Test   public  void  link押下でwmcが非表示にされる()  {      tester.startPage(new  HomePage());      tester.clickLink("link");      //  linkクリックでAjaxでwmcが更新されるか      tester.assertComponentOnAjaxResponse("wmc");      //  wmcが非表示になったか      tester.assertInVisible("wmc");   } 使用例 assert
  16. 16. WicketTester #executeAjaxEvent tester.executeAjaxEvent("id",  String); idのコンポーネントでStringのイベントが実行される。 イベントは"onBlur"などのJSイベント。 ビヘイビアなどのテストに用いる。 @Test   public  void  clickイベントでラベルが書き換わる(){      tester.startPage(HomePage.class);      tester.assertLabel("label",  "イベント発生前");          //  Ajaxの  click  イベントを実行      tester.executeAjaxEvent("label",  "click");      tester.assertLabel("label",  "イベント発生後");   } 使用例
  17. 17. WicketTester #executeAllTimerBehaviors tester.executeAllTimerBehaviors(MarkupContainer); 引数のコンポーネント以下の全てのTimerBehaviorが実行される。 個別に実行したいときは、executeBehavior(Behavior)を使う。 @Test   public  void  TimerBehaviorでclockが更新される()  {      WebPage  page  =  tester.startPage(new  AjaxTimerPage());      //  PageのTimerBehaviorを全て動作させる.      tester.executeAllTimerBehaviors(page);      //  TimerBehaviorがaddされたコンポーネントが更新されるか.      tester.assertComponentOnAjaxResponse("clock");   } 使用例
  18. 18. WicketTester #startComponentInPage tester.startComponentInPage(Component); 引数に渡したコンポーネントのテスト用の 実行環境とメソッドが提供される。 @Test   public  void  Panelのラベルが正しく表示される()  {      //  コンポーネント(FooPanel)のテスト開始      tester.startComponentInPage(new  FooPanel("foo"));      //  FooPanel内のLabel1が表示されるか      tester.assertLabel("foo:label1",  "Hello!"));   } 使用例 注1)階層構造になっているwicket:idは、 id1:id2 の様にコロンで結合する 注2) 事前にページをスタートしておかなくても、コンポーネント単体でテストできる
  19. 19. WicketTester その他 tester.assertComponent("id",  Class); idのコンポーネントのクラスが引数と一致するかがassertされる assert tester.assertModelValue("id",  Object); idのコンポーネントのモデルの値が引数と一致するかがassertされる など。 tester.getLastRenderedPage();   tester.getComponentFromLastRenderedPage("id"); テスト中のページやコンポーネントを取得する
  20. 20. Formについての ユニットテスト機能
  21. 21. WicketTester#newFormTester tester.newFormTester("form",  false); Formのテスト用の実行環境とメソッドが提供される。 第2引数は子のForm用コンポーネントの入力値を空にするかどうか
 (省略するとtrue) @Test   public  void  FormTest1()  {      tester.startPage(new  HomePage());      //  Formのテスト開始        FormTester  formTester  =  tester.newFormTester("form");   } 使用例
  22. 22. FormTester Form用コンポーネントの操作 formTester.setValue("id",  String); idのForm用コンポーネントにStringの値をセットする。 TextField, TextAreaなどのテキスト入力用。 formTester.select("id",  int); idのForm用コンポーネントのint番目を選択する。 RadioChoiceなどの単数選択用。 formTester.selectMultiple("id",  int...); idのForm用コンポーネントのint番目を全て選択する。 CheckBoxMultipleChoiceなどの複数選択用。 注)他に、ファイル送信用のsetFileや、個別のラジオボタン用のsetValueなどもある。
  23. 23. FormTester#submit  formTester.submit(); FormのonSubmitメソッドが実行される。 @Test   public  void  FormをsubmitするとLabelに入力値が表示される()  {      tester.startPage(new  FormPage());      //  nameLabelは最初は空      tester.assertLabel("nameLabel",  "");      FormTester  formTester  =  tester.newFormTester("form");      formTester.setValue("name",  "foo");      formTester.submit();      //  nameLabelにフォームからsubmitされた入力値が表示される      tester.assertLabel("nameLabel",  "foo");   } 使用例
  24. 24. WicketTester #assertFeedback tester.assertFeedback("id",  String...); assert idのFeedbackPanelに表示されるメッセージが 引数のString配列と一致するかがassertされる。 なお、ページにセットされたメッセージをテストするときには、 assertInfoMessages(String...)   assertErrorMessages  (String...) なども利用できる。
  25. 25. Sessionについての ユニットテスト機能
  26. 26. WicketTester#getSession tester.getSession(); Sessionを取得できる。テストの前準備でSessionに値を設定したり、 assert用にテスト対象実行後のSessionの値を取得したりに使える。 @Test   public  void  ログインしていなければエラーページが表示される()  {      MySession  session  =  (MySession)  tester.getSession();      session.setSigned(false);      tester.startPage(SignedPage.class);      tester.assertRenderedPage(ErrorPage.class);   } 使用例
  27. 27. WicketTester での 実践的なユニットテスト
  28. 28. WicketTester 実際には • 実際のコードでは、モデルの生成やクリックイベントに他の クラスとの依存(DBとのやりとり、etc...)が絡んでくる。 • ユニットテストでは、こうしたコードをテストダブル(モック やスタブ)に置き換えてテストを実行する。 • WicketTesterでも同様に、テストダブルを用いたテストを 実行できる。 • Wicketには Spring、Guice、CDIなどのDIコンテナとの 正式な連係用APIがある。 DIコンテナを利用したい場合はこ れも利用できる。 注)DIコンテナを利用するときは、JVMなどのメモリチューニングなどが必要なこともある(MaxPermSizeなど)
  29. 29. Guice、Springとの連係の例 <dependency>      <groupId>org.apache.wicket</groupId>      <artifactId>wicket-­‐guice</artifactId>      <version>6.12.0</version>   </dependency>   <dependency>      <groupId>com.google.inject</groupId>      <artifactId>guice</artifactId>      <version>3.0</version>   </dependency> Guiceの場合の pom.xml <dependency>      <groupId>org.apache.wicket</groupId>      <artifactId>wicket-­‐spring</artifactId>      <version>6.12.0</version>   </dependency>   <dependency>      <groupId>org.springframework</groupId>      <artifactId>spring-­‐context</artifactId>      <version>3.2.5.RELEASE</version>   </dependency> Springの場合の pom.xml
  30. 30. Guiceとの連係の例 @Override   public  void  init()  {      super.init();      /*  中略  */      initGuice();   }       protected  void  initGuice()  {      //  Wicketから呼び出されたクラスのインジェクションを実行する      getComponentInstantiationListeners().add(new  GuiceComponentInjector(this));   } WebApplicationのサブクラス
  31. 31. Guiceとの連係の例 public  class  BazPage  extends  WebPage  {          //Guiceでインジェクションしたい変数に@Inject      @Inject      private  IFoo  foo;          /*  中略  */   } Wicketのページやコンポーネント //  標準でバインドされる実装クラスを設定しておく@ImplementeBy   @ImplementedBy(Foo.class)   public  interface  IFoo  {      public  boolean  bar();          /*  中略  */   } インジェクションの対象になるインターフェースなど 通常利用するなら、設定はこれだけ。
  32. 32. Guiceとの連係の例(テストの準備) テスト用のクラス @Before   public  void  setUp()  {      tester  =  new  WicketTester(new  WicketApplication()  {          @Override          protected  void  initGuice()  {              //  テストダブル用のModuleを作成              Module  module  =  new  Module()  {                  @Override                  public  void  configure(Binder  binder)  {                      IFoo  foo  =  mock(IFoo.class);                      //  IFooクラスにテストダブルをバインドする                      binder.bind(IFoo.class).toInstance(foo);                  }              };              getComponentInstantiationListeners().add(new  GuiceComponentInjector(this,  module));          }      });   } 匿名クラスでWebApplicationのサブクラスの initGuiceメソッドをテスト用に拡張 テストダブルが優先してインジェクションされるようにする↓ ←テストダブルを設定 ユニットテスト自体の方法やメソッドは変わらず。
  33. 33. Springとの連係の例 @Override   public  void  init()  {          /*  中略  */          initSpring();   }       protected  void  initSpring()  {      AnnotationConfigApplicationContext  ctx  =  new  AnnotationConfigApplicationContext();      //  アノテーション付きのBeanを下のパッケージから検索する      ctx.scan("com.exmaple");      ctx.refresh();      getComponentInstantiationListeners().add(new  SpringComponentInjector(this,  ctx));   } WebApplicationのサブクラス 注)アノテーションでのbean定義を行う場合。   ApplicationContextの設定次第で、従来のxmlでの定義なども勿論可能。
  34. 34. Springとの連係の例 public  class  BazPage  extends  WebPage  {          //Springでインジェクションしたい変数に@SpringBean      @SpringBean      private  IFoo  foo;          /*  中略  */   } Wicketのページやコンポーネント //  Springのbean定義として登録する。@Componentなどでも勿論OK。   @Service   public  class  Foo  implements  IFoo  {      @Override      public  boolean  bar()  {          /*  実装は中略  */      }   } インターフェースの実装クラスなど 通常利用するなら、設定はこれだけ。
  35. 35. Springとの連係の例(テストの準備) テスト用のクラス @Before   public  void  setUp()  {      tester  =  new  WicketTester(new  SpringApplication()  {          @Override          protected  void  initSpring()  {              //  テストダブル作成              IFoo  foo  =  Mockito.mock(IFoo.class);              //  テストダブル用のApplicationContextMockを用意し、テストダブルを追加              ApplicationContextMock  ctxm  =  new  ApplicationContextMock();              ctxm.putBean("foo",  foo);              getComponentInstantiationListeners().add(new  SpringComponentInjector(this,  ctxm));          }      });   }   匿名クラスでWebApplicationのサブクラスの initSpringメソッドをテスト用に拡張 テストダブルが優先してインジェクションされるようにする↑ ←テストダブルを  を設定 ユニットテスト自体の方法やメソッドは変わらず。

×