apache wicketのユニットテスト機能

35
Wicketのユニットテスト機能 (WicketTester) Wicket-Sapporo 2013-11-21 rev,2 @gishi_yama

Upload: hiroto-yamakawa

Post on 09-Jan-2017

463 views

Category:

Engineering


1 download

TRANSCRIPT

Wicketのユニットテスト機能 (WicketTester)Wicket-Sapporo 2013-11-21 rev,2

@gishi_yama

Wicketには ページやコンポーネントのユニットテストを補助する 機能が備わっている

ユニットテスト 詳しくは

ユニットテスト とは"...ユニットテストでは、対象のクラスやメソッドが期待された振る舞いをするか検証し、テストが成功することによってそれを保証します。この「期待された振る舞い」とは、言い換えれば、対象のクラスやメソッドの仕様です。"

(渡辺修司, "JUnit実践入門 体系的に学ぶユニットテストの技法", 技術評論社, 初版, pp.29)

"...ユニットテストを繰り返し何度も実行することで、プログラムに問題が発生したときに、早い段階で影響範囲などをチェックできます。...リスク回避のために避けてきた機能拡張やコードの修正を安心して行う事ができます。"(渡辺修司, "JUnit実践入門 体系的に学ぶユニットテストの技法", 技術評論社, 初版, pp.30-31)

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));      }        }

期待通りの振る舞いをしているかプログラムで自動的に確認

画像出典 : upload.wikimedia.org

WicketTester

WicketTesternew  WicketTester(WebApplication);

引数に渡されたWebApplicationのインスタンスをもとに、 プロジェクトがあたかもWebサーバ上で動いている様な

テスト用の実行環境とメソッドが提供される。 JUnitテストクラスのフィールド変数に定義しておくと楽。

private  WicketTester  tester;      @Before  public  void  setUp(){      tester  =  new  WicketTester(new  WicketApplication());  }

使用例

画面表示についての ユニットテスト機能

WicketTester#startPagetester.startPage(WebPage);

引数に渡されたページのテストが開始(ページが表示*)される。 各テストを行う前の準備段階として実行する。

引数は、ページのインスタンスもしくはクラスでもよい。

@Test  public  void  Test1()  {      //  ページのテスト開始(ページの表示)      tester.startPage(new  HomePage());  }

使用例

*ここでの「表示」はもちろん仮想的な意味であって、ブラウザでアクセスできるわけではない

WicketTester #assertRenderedPage

tester.assertRenderedPage(Class);

最後に表示されるページのクラスが引数と一致するかがassertされる。 ページが正常に描画されたかどうか、という意味でのテストに加え、 後述するLinkやFormのページ遷移結果のテストにも用いる。 スーパークラスが渡された場合も一致と見なされるので注意。

@Test  public  void  初期状態でページが表示される()  {  

   //  HomePageのテストの開始(ページの表示)      tester.startPage(new  HomePage());      //  画面に表示されるページが  HomePage.class  のページか      tester.assertRenderedPage(HomePage.class);  }

使用例

assert

WicketTester#assertLabeltester.assertLabel("id",  String);

idのラベルに表示されている文字列がStringと一致するかがassertされる。

@Test  public  void  ラベルに正しく文字列が表示される(){     tester.startPage(HomePage.class);     //message1に  こんにちは!  が表示されるか  

  tester.assertLabel("message1",  "こんにちは!");  }

使用例

assert

WicketTester#executeUrltester.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");  }

使用例

WicketTester#clickLinktester.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);  }

使用例

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

WicketTester #assertComponentOnAjaxResponsetester.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

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",  "イベント発生後");  }

使用例

WicketTester #executeAllTimerBehaviorstester.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");  }

使用例

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) 事前にページをスタートしておかなくても、コンポーネント単体でテストできる

WicketTester その他tester.assertComponent("id",  Class);

idのコンポーネントのクラスが引数と一致するかがassertされる

assert

tester.assertModelValue("id",  Object);

idのコンポーネントのモデルの値が引数と一致するかがassertされる

など。

tester.getLastRenderedPage();  tester.getComponentFromLastRenderedPage("id");

テスト中のページやコンポーネントを取得する

Formについての ユニットテスト機能

WicketTester#newFormTester

tester.newFormTester("form",  false);

Formのテスト用の実行環境とメソッドが提供される。 第2引数は子のForm用コンポーネントの入力値を空にするかどうか

(省略するとtrue)

@Test  public  void  FormTest1()  {      tester.startPage(new  HomePage());      //  Formのテスト開始        FormTester  formTester  =  tester.newFormTester("form");  }

使用例

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などもある。

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");  }

使用例

WicketTester #assertFeedback

tester.assertFeedback("id",  String...);

assert

idのFeedbackPanelに表示されるメッセージが 引数のString配列と一致するかがassertされる。

なお、ページにセットされたメッセージをテストするときには、 assertInfoMessages(String...)   assertErrorMessages  (String...)

なども利用できる。

Sessionについての ユニットテスト機能

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);  }

使用例

WicketTester での 実践的なユニットテスト

WicketTester 実際には

•実際のコードでは、モデルの生成やクリックイベントに他のクラスとの依存(DBとのやりとり、etc...)が絡んでくる。

•ユニットテストでは、こうしたコードをテストダブル(モックやスタブ)に置き換えてテストを実行する。

•WicketTesterでも同様に、テストダブルを用いたテストを実行できる。

•Wicketには Spring、Guice、CDIなどのDIコンテナとの正式な連係用APIがある。 DIコンテナを利用したい場合はこれも利用できる。

注)DIコンテナを利用するときは、JVMなどのメモリチューニングなどが必要なこともある(MaxPermSizeなど)

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

Guiceとの連係の例@Override  public  void  init()  {      super.init();      /*  中略  */      initGuice();  }      protected  void  initGuice()  {      //  Wicketから呼び出されたクラスのインジェクションを実行する      getComponentInstantiationListeners().add(new  GuiceComponentInjector(this));  }

WebApplicationのサブクラス

Guiceとの連係の例

public  class  BazPage  extends  WebPage  {          //Guiceでインジェクションしたい変数に@Inject      @Inject      private  IFoo  foo;          /*  中略  */  }

Wicketのページやコンポーネント

//  標準でバインドされる実装クラスを設定しておく@ImplementeBy  @ImplementedBy(Foo.class)  public  interface  IFoo  {      public  boolean  bar();          /*  中略  */  }

インジェクションの対象になるインターフェースなど

通常利用するなら、設定はこれだけ。

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メソッドをテスト用に拡張

テストダブルが優先してインジェクションされるようにする↓

←テストダブルを設定

ユニットテスト自体の方法やメソッドは変わらず。

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での定義なども勿論可能。

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()  {          /*  実装は中略  */      }  }

インターフェースの実装クラスなど

通常利用するなら、設定はこれだけ。

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メソッドをテスト用に拡張

テストダブルが優先してインジェクションされるようにする↑

←テストダブルを  を設定

ユニットテスト自体の方法やメソッドは変わらず。