Cubby のテストを S2JUnit4 環境で行う
Cubby のアクションのテストを S2JUnit4 環境で行う方法を試してみました。テストコードはこんな感じになりますです。
@RunWith(Seasar2.class)
public class IndexActionTest {
public TestContext ctx;
public CubbyRunner cubbyRunner;
@Test
public void index() throws Exception{
ActionResult actualResult = cubbyRunner.run("/");
CubbyAssert.assertPathEquals(Forward.class, "index.jsp", actualResult);
}
}Eclipse で Run/Debug 設定などいじらなくても、メソッド単位の実行が出来て良い感じです。(Run/Debug 設定をクラス毎に JUnit3 のランナーに変える以外に方法を知らなかったので。。。)
書いたコード
書いたのは三つ。まずインターフェース。CubbyTestCase#processAction に該当する所。
public interface CubbyRunner {
public ActionResult run(String path) throws Exception;
}次に実装クラス。ほぼ CubbyTestCase ですが、request などをメソッド呼び出ししている部分を Injection されるように変更。(元は Cubby 1.1.0 の TestCase)
public class CubbyRunnerImpl implements CubbyRunner {
@Binding
private Router router;
@Binding
private ActionProcessor actionProcessor;
@Binding
private MockHttpServletRequest request;
@Binding
private MockHttpServletResponse response;
@Binding
private MockServletContext application;
public ActionResult run(String originalPath) throws Exception {
final MockHttpServletRequest request = this.request;
setServletPath(request, originalPath);
final MockHttpServletResponse response = this.response;
routing(request, response);
setupThreadContext();
final ActionResultWrapper actionResultWrapper = actionProcessor.process(request,
response);
if (actionResultWrapper == null) {
return null;
}
return actionResultWrapper.getActionResult();
}
protected String routing(final MockHttpServletRequest request,
final MockHttpServletResponse response) {
final InternalForwardInfo internalForwardInfo = router.routing(request,
response);
if (internalForwardInfo == null) {
throw new RuntimeException(request.getServletPath()
+ " could not mapping to action");
}
final String internalForwardPath = internalForwardInfo.getInternalForwardPath();
final MockHttpServletRequest internalForwardRequest = this.application.createRequest(internalForwardPath);
request.setAttribute(ATTR_ROUTINGS,
internalForwardInfo.getOnSubmitRoutings());
request.setAttribute("javax.servlet.forward.request_uri",
request.getRequestURI());
request.setAttribute("javax.servlet.forward.context_path",
request.getContextPath());
request.setAttribute("javax.servlet.forward.servlet_path",
request.getServletPath());
request.setAttribute("javax.servlet.forward.path_info",
request.getPathInfo());
request.setAttribute("javax.servlet.forward.query_string",
request.getQueryString());
final String servletPath = internalForwardRequest.getServletPath();
setServletPath(request, servletPath);
request.setQueryString(internalForwardRequest.getQueryString());
if (StringUtil.isNotBlank(internalForwardRequest.getQueryString())) {
final Map<String, List<String>> pathParameters = parseQueryString(internalForwardRequest.getQueryString());
for (final Entry<String, List<String>> entry : pathParameters.entrySet()) {
final String name = entry.getKey();
for (final String value : entry.getValue()) {
request.addParameter(name, value);
}
}
}
return internalForwardPath;
}
private static void setServletPath(final MockHttpServletRequest request,
final String servletPath) {
final Field servletPathField = ClassUtil.getDeclaredField(request.getClass(),
"servletPath");
servletPathField.setAccessible(true);
try {
servletPathField.set(request, servletPath);
} catch (final Exception ex) {
throw new RuntimeException(ex);
}
}
protected void setupThreadContext() {
ThreadContext.setRequest(this.request);
}
private Map<String, List<String>> parseQueryString(final String queryString) {
final Map<String, List<String>> params = new HashMap<String, List<String>>();
final String[] tokens = queryString.split("&");
for (final String token : tokens) {
final String[] param = token.split("=");
final String name = param[0];
final String value = param[1];
final List<String> values;
if (params.containsKey(name)) {
values = params.get(name);
} else {
values = new ArrayList<String>();
params.put(name, values);
}
values.add(value);
}
return params;
}
}CubbyRunner に入れてもよいのですが、何となく流儀にしたがって CubbyAssert 。
public class CubbyAssert extends S2Assert {
public static void assertPathEquals(
final Class<? extends ActionResult> resultClass,
final String expectedPath,
final ActionResult actualResult) {
assertEquals("ActionResultの型をチェック",
resultClass,
actualResult.getClass());
if (actualResult instanceof Forward) {
assertEquals("パスのチェック",
expectedPath,
((Forward) actualResult).getPath());
} else if (actualResult instanceof Redirect) {
assertEquals("パスのチェック",
expectedPath,
((Redirect) actualResult).getPath());
}
}
}
設定周り
やったのは、s2junit4-cubby.dicon の作成と s2junit4.dicon への設定の追加。
まず、s2junit4-cubby.dicon 。CubbyRunner のコンポーネント設定と、Router と ActionProcessor を Injection するため、cubby.dicon と routing.dicon を include 。
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE components PUBLIC "-//SEASAR//DTD S2Container 2.4//EN" "http://www.seasar.org/dtd/components24.dtd"> <components namespace="s2junit4-cubby"> <include path="cubby.dicon" /> <include path="routing.dicon" /> <component class="unit.CubbyRunnerImpl" /> </components>
s2junit4.dicon は ここ のを少しカスタマイズ。ConfigFileIncluderImpl に app.dicon と上記の s2juni4-cubby.dicon を追加。
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE components PUBLIC "-//SEASAR//DTD S2Container 2.4//EN" "http://www.seasar.org/dtd/components24.dtd"> <components namespace="s2junit4"> <component name="context" class="org.seasar.framework.unit.impl.InternalTestContextImpl"> <property name="ejb3Enabled">false</property> </component> <component class="org.seasar.framework.unit.impl.DataAccessorImpl" /> <component class="org.seasar.framework.unit.impl.ConfigFileIncluderImpl"> <initMethod name="addConfigFile"> <arg>"app.dicon"</arg> </initMethod> <initMethod name="addConfigFile"> <arg>"s2junit4-cubby.dicon"</arg> </initMethod> <initMethod name="addConfigFile"> <arg>context.testClassShortName + ".dicon"</arg> </initMethod> </component> :
実行コードはほとんど変えず、クラスの乗せ換えと dicon の設定だけでいけました。ぼつぼつこっちに置き換えてみて問題ないか確かめよう。