拡張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(); } }