Locked History Actions

wicket/ExWicketTester

拡張Wicketテスタ

ここでは、Wicketに標準で備わっているひどいテスタの代わりに、タイプセーフでリファクタリングに強い代替のテスタを提案してみる。

ひどいWicketプログラムとそのテスト

ひどいとは言っても、ごく普通に書くとこうなる。

<html>
<title>Login</title>
<body>
<div wicket:id="feedback"></div>
<form wicket:id="loginForm">
<table border="0">
 <tr><td>メールアドレス</td><td><input type="text" wicket:id="email"/></td></tr>
 <tr><td>パスワード</td><td><input type="password" wicket:id="pass"/></td></tr>
 <tr align="right"><td colspan="2"><input type="submit" value="ログイン"/></td></tr>
</table>
</form>
</body>
</html>

import org.apache.wicket.markup.html.*;
import org.apache.wicket.markup.html.form.*;
import org.apache.wicket.markup.html.panel.*;
import org.apache.wicket.model.*;

public class LoginPage extends WebPage {
  
  public LoginPage() {    
    add(new FeedbackPanel("feedback"));
    add(new LoginForm());
  }
  
  class LoginForm extends Form<Void> {    
    
    private LoginForm() {
      super("loginForm");
      add(new TextField<String>("email", new Model<String>()));
      add(new PasswordTextField("pass", new Model<String>()));
    }    
    
    @Override
    public void onSubmit() {
      String mail = get("email").getDefaultModelObjectAsString();
      String pass = get("pass").getDefaultModelObjectAsString();
      
      // login success or failed ....     
    }
  }
}

import org.apache.wicket.util.tester.*;
import org.junit.*;

public class LoginTest {

  @Test
  public void test() {
    WicketTester tester = new WicketTester();
    tester.startPage(LoginPage.class);
    tester.assertRenderedPage(LoginPage.class);
    tester.assertNoErrorMessage();
    
    FormTester formTester = tester.newFormTester("loginForm");
    formTester.setValue("email", "sysdba");
    formTester.setValue("pass", "masterkey");
    formTester.submit();
    tester.assertNoErrorMessage();
    
  }
}

LoginPageの改良

少なくともLoginPageクラスは以下のようにし、文字列ではなく文字列定数を参照すべきだろう。

import org.apache.wicket.markup.html.*;
import org.apache.wicket.markup.html.form.*;
import org.apache.wicket.markup.html.panel.*;
import org.apache.wicket.model.*;

public class LoginPage extends WebPage {
  
  static final String ID_FEEDBACK = "feedback";
  static final String ID_LOGINFORM = "loginForm";
  static final String ID_EMAIL = "email";
  static final String ID_PASS = "pass";
  
  public LoginPage() {    
    add(new FeedbackPanel(ID_FEEDBACK));
    add(new LoginForm());
  }
  
  class LoginForm extends Form<Void> {    
    
    private LoginForm() {
      super(ID_LOGINFORM);
      add(new TextField<String>(ID_EMAIL, new Model<String>()));
      add(new PasswordTextField(ID_PASS, new Model<String>()));
    }    
    
    @Override
    public void onSubmit() {
      String mail = get(ID_EMAIL).getDefaultModelObjectAsString();
      String pass = get(ID_PASS).getDefaultModelObjectAsString();
      
      // login success or failed ....     
    }
  }
}

さらにLoginPageTestも以下のように、その文字列定数を参照させる。

import org.apache.wicket.util.tester.*;
import org.junit.*;
import static test.LoginPage.*;

public class LoginTest {

  @Test
  public void test() {
    WicketTester tester = new WicketTester();
    tester.startPage(LoginPage.class);
    tester.assertRenderedPage(LoginPage.class);
    tester.assertNoErrorMessage();
    
    FormTester formTester = tester.newFormTester(ID_LOGINFORM);
    formTester.setValue(ID_EMAIL, "sysdba");
    formTester.setValue(ID_PASS, "masterkey");
    formTester.submit();
    tester.assertNoErrorMessage();
    
  }
}

本当はこのようにしたい

LoginPage側では、テストで参照されるであろうコンポーネントをフィールドとして保持しておく。 これは問題ない。

import org.apache.wicket.markup.html.*;
import org.apache.wicket.markup.html.form.*;
import org.apache.wicket.markup.html.panel.*;
import org.apache.wicket.model.*;

public class LoginPage extends WebPage {
  
  static final String ID_FEEDBACK = "feedback";
  static final String ID_LOGINFORM = "loginForm";
  static final String ID_EMAIL = "email";
  static final String ID_PASS = "pass";
  
  LoginForm loginForm;
  
  public LoginPage() {    
    add(new FeedbackPanel(ID_FEEDBACK));
    add(loginForm = new LoginForm());
  }
  
  class LoginForm extends Form<Void> {    
    
    TextField<String>emailField;
    PasswordTextField passField;
    
    private LoginForm() {
      super(ID_LOGINFORM);
      add(emailField = new TextField<String>(ID_EMAIL, new Model<String>()));
      add(passField = new PasswordTextField(ID_PASS, new Model<String>()));
    }    
    
    @Override
    public void onSubmit() {
      String mail = emailField.getDefaultModelObjectAsString();
      String pass = passField.getDefaultModelObjectAsString();
      
      // login success or failed ....     
    }
  }
}

テストの方は以下のようにしたい。ID文字列は一切使わず、コンポーネントのみを参照する。 これはWicket付属のテスターではできない。

public class LoginTest {

  @Test
  public void test() {
    WicketTester tester = new WicketTester();
    tester.startPage(LoginPage.class);
    tester.assertRenderedPage(LoginPage.class);
    tester.assertNoErrorMessage();
    
    LoginPage loginPage = tester.getLastRenderedPage();
    LoginForm loginForm = loginPage.loginForm;  
    
    FormTester formTester = tester.newFormTester(loginForm);
    formTester.setValue(loginForm.emailField, "sysdba");
    formTester.setValue(loginForm.passField, "masterkey");
    formTester.submit();
    tester.assertNoErrorMessage();
    
  }
}

拡張Wicketテスタ

ということで、ソースはこちらこれを使うとテストユニットは次のように書ける。

import org.junit.*;

import test.LoginPage.*;

import com.cm55.third.wicket.test.*;

public class LoginTest {

  @Test
  public void test() {
    ExWicketTester tester = new ExWicketTester();
    LoginPage loginPage = tester.startPage(LoginPage.class);
    tester.assertRenderedPage(LoginPage.class);
    tester.assertNoErrorMessage();
    
    LoginForm loginForm = loginPage.loginForm;    
    ExFormTester<LoginForm> formTester = tester.newFormTester(loginForm);
    formTester.setValue(loginForm.emailField, "sysdba");
    formTester.setValue(loginForm.passField, "masterkey");
    formTester.submit();
    tester.assertNoErrorMessage();    
  }
}