Actionのテスト

http://sweetbat.ddo.jp/miraque/をプレーンJavaからSAStrutsに変えてから、まだJUnitでのテストをしていない。
Actionのテストはしたいとかずっと思っていたけどうまく動作しないので放置していた。

だけどhttp://sweetbat.ddo.jp/miraque/も安定動作しはじめたので今後の為にテストが出来るようになっておきたい。
とおもってやったんだけど、ServiceのDIがうまく働いてくれない・・・

package miraque.action;

import miraque.service.TLogService;
import org.junit.Test;
import org.seasar.extension.unit.S2TestCase;
import org.seasar.struts.util.RequestUtil;

public class RouteActionTest extends S2TestCase {

	TLogService tlogService;

	RouteAction action;

	public void before() throws Exception {
		action = new RouteAction();
	}

	@Test
	public void testIndex() {

		action.tlogService = tlogService;
		action.request = RequestUtil.getRequest();
		action.context = getServletContext();

		// テスト実施
		String rtn = action.index();
		// 判定
		assertNotNull("遷移先が null.", rtn);
		assertEquals("list.jsp へ遷移されない", "list.jsp", rtn);

	}
}

これを実行すると、

java.lang.NoClassDefFoundError: miraque/service/TlogService (wrong name: miraque/service/TLogService)
	at java.lang.ClassLoader.defineClass1(Native Method)
	at java.lang.ClassLoader.defineClass(ClassLoader.java:621)
	at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:124)
	at java.net.URLClassLoader.defineClass(URLClassLoader.java:260)
	at java.net.URLClassLoader.access$000(URLClassLoader.java:56)
	at java.net.URLClassLoader$1.run(URLClassLoader.java:195)
	at java.security.AccessController.doPrivileged(Native Method)
	at java.net.URLClassLoader.findClass(URLClassLoader.java:188)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:307)
	at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:300)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:252)
	at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:320)
	at java.lang.Class.forName0(Native Method)
	at java.lang.Class.forName(Class.java:247)
	at org.seasar.framework.util.ClassUtil.forName(ClassUtil.java:98)
	at org.seasar.framework.convention.impl.NamingConventionImpl.findClass(NamingConventionImpl.java:589)
	at org.seasar.framework.convention.impl.NamingConventionImpl.fromComponentNameToClass(NamingConventionImpl.java:515)
	at org.seasar.framework.container.creator.ComponentCreatorImpl.createComponentDef(ComponentCreatorImpl.java:281)
	at org.seasar.framework.container.hotdeploy.HotdeployBehavior.createComponentDef(HotdeployBehavior.java:195)
	at org.seasar.framework.container.hotdeploy.HotdeployBehavior.getComponentDef(HotdeployBehavior.java:141)
	at org.seasar.framework.container.impl.S2ContainerBehavior$DefaultProvider.acquireFromHasComponentDef(S2ContainerBehavior.java:172)
	at org.seasar.framework.container.impl.S2ContainerBehavior.acquireFromHasComponentDef(S2ContainerBehavior.java:86)
	at org.seasar.framework.container.impl.S2ContainerImpl.hasComponentDef(S2ContainerImpl.java:456)
	at org.seasar.framework.unit.S2FrameworkTestCase.bindField(S2FrameworkTestCase.java:691)
	at org.seasar.framework.unit.S2FrameworkTestCase.bindFields(S2FrameworkTestCase.java:672)
	at org.seasar.framework.unit.S2FrameworkTestCase.runBare(S2FrameworkTestCase.java:304)
	at junit.framework.TestResult$1.protect(TestResult.java:106)
	at junit.framework.TestResult.runProtected(TestResult.java:124)
	at junit.framework.TestResult.run(TestResult.java:109)
	at junit.framework.TestCase.run(TestCase.java:120)
	at junit.framework.TestSuite.runTest(TestSuite.java:230)
	at junit.framework.TestSuite.run(TestSuite.java:225)
	at org.junit.internal.runners.JUnit38ClassRunner.run(JUnit38ClassRunner.java:81)
	at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:45)
	at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:460)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:673)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:386)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:196)

という例外が発生してしまう。

これは以下のようにすると直ったけれども、なぜ?!

protected TLogService tLogService;

lがLだといいみたい。

だけど

指定されたコンポーネント名(tLogService)はクラス(miraque.service.TLogService)のコンポーネント名(TLogService)とマッチしません (大文字・小文字の違いに注意してください)
DEBUG 2009-07-11 14:18:22,156 [main] クラス(miraque.service.TLogService[TLogService])のコンポーネント定義を登録します
DEBUG 2009-07-11 14:18:22,421 [main] クラス(miraque.service.TNumService[TNumService])のコンポーネント定義を登録します

って怒られているんだよね。でも登録してくれているよう。。。
でも例外は以前として発生していたり・・・

java.lang.NullPointerException
	at org.seasar.struts.customizer.ActionCustomizer.customize(ActionCustomizer.java:81)
	at org.seasar.framework.container.customizer.CustomizerChain.doCustomize(CustomizerChain.java:140)
	at org.seasar.framework.container.customizer.AbstractCustomizer.customize(AbstractCustomizer.java:145)
	at org.seasar.framework.container.creator.ComponentCreatorImpl.customize(ComponentCreatorImpl.java:309)
	at org.seasar.framework.container.creator.ComponentCreatorImpl.createComponentDef(ComponentCreatorImpl.java:269)
	at org.seasar.framework.container.hotdeploy.HotdeployBehavior.createComponentDef(HotdeployBehavior.java:178)
	at org.seasar.framework.container.hotdeploy.HotdeployBehavior.getComponentDef(HotdeployBehavior.java:139)
	at org.seasar.framework.container.impl.S2ContainerBehavior$DefaultProvider.acquireFromHasComponentDef(S2ContainerBehavior.java:172)
	at org.seasar.framework.container.impl.S2ContainerBehavior.acquireFromHasComponentDef(S2ContainerBehavior.java:86)
	at org.seasar.framework.container.impl.S2ContainerImpl.hasComponentDef(S2ContainerImpl.java:456)
	at org.seasar.framework.unit.S2FrameworkTestCase.bindField(S2FrameworkTestCase.java:709)
	at org.seasar.framework.unit.S2FrameworkTestCase.bindFields(S2FrameworkTestCase.java:672)
	at org.seasar.framework.unit.S2FrameworkTestCase.runBare(S2FrameworkTestCase.java:304)
	at junit.framework.TestResult$1.protect(TestResult.java:106)
	at junit.framework.TestResult.runProtected(TestResult.java:124)
	at junit.framework.TestResult.run(TestResult.java:109)
	at junit.framework.TestCase.run(TestCase.java:120)
	at junit.framework.TestSuite.runTest(TestSuite.java:230)
	at junit.framework.TestSuite.run(TestSuite.java:225)
	at org.junit.internal.runners.JUnit38ClassRunner.run(JUnit38ClassRunner.java:81)
	at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:45)
	at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:460)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:673)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:386)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:196)

でもただ単にactionの初期化がされていなかっただけだったとか・・・ケアレスミス・・・

	@Before
	public void setUp() throws Exception {
		include("app.dicon");
		action = new RouteAction();
		super.setUp();
	}

後DBの更新がある場合 testXxxTxとするとロールバックもしてくれた!

	@Test
	public void testIndexTx() {

http://d.hatena.ne.jp/h-kageyu/20090413/1239590941

0.9.0リリースまでにちょこちょこと実装

文字コード

http://sweetbat.ddo.jp/miraque/をプレーンJavaからSAStrutsに切り替えるまで、文字コードWindows-31Jを使用していました。主に携帯電話向けに作ったサイトだからそのほうが無難だったからです。
でもmobyletに出会ってからはそのような心配は必要なくなりそうです。
なので自分のソースを全てUTF-8(javajspも)変更してテストしてみました。
AUDoCoMoでは、Shift-JISで、SoftbankではUTF-8で、その他ではUTF-8になっていました!
なので

<meta http-equiv="Content-Type" content="text/html; charset=xxxx" />

は必要ないようです。あるとブラウザが混乱するかもしれません^^;

キャリア取得

mobyletを使うとキャリア取得する関数が実装されています。

Mobylet mobylet = MobyletFactory.getInstance();
Carrier carrier = mobylet.getCarrier();

if (carrier.equals(Carrier.DOCOMO)) {
	// DoCoMoの時の処理
} else if (carrier.equals(Carrier.AU)) {
	// AUの時の処理
} else if (carrier.equals(Carrier.SOFTBANK)) {
	// Softbankの時の処理
} else if (carrier.equals(Carrier.OTHER)) {
	// 上記以外時の処理
}

個体識別番号の取得

個体識別番号の取得方法

Mobylet mobylet = MobyletFactory.getInstance();

String id = mobylet.getUid();

DoCoMoだとURLに「?guid=ON」を付与しないと取得ができません

http://hogehoge.com/?guid=ON

<s:form action="/?guid=ON">

ログ出力インターセプターの実装

ログをDB(PostgreSQL)に出力する実装をした。
今までインターセプターを使わずに

	@Execute(validator = false)
	public String index() {
		try {
			// 絵文字の設定をする。
			init(request, context);

			// アクセスカウンターにアクセスする
			counter = tNumService.getAccessCounter();

			// 管理者リンクの表示有無
			visibleadmin = Utils.isAdmin(request);

			// ログ出力
			tlogService.insertLog(request);

			return pageIndex;
		} catch(Exception e) {

			// 例外処理
			procException(request, e);
			return "/error.jsp";

		}
	}

という風に

			// ログ出力
			tlogService.insertLog(request);

を各アクションに入れて対応していました。
理由は、Interceptorの導入方法がわからないから(公式ページを見てもわからなかった)
ネットで調べてみていろいろ試行錯誤して、aop.diconを作ってみたり、app.diconを変更してみたりしたけれども、結局diconファイルを一切弄わないでいいようでした^^;

まずInterceptorしたい関数に以下のようにアノテーションを追加する。

	@Execute(validator = false)
	@Aspect("loggerInterceptor")
	public String index() {

そしてinterceprtorパッケージに以下のようなクラスを作成する

public class LoggerInterceptor extends AbstractInterceptor {
	private static final long serialVersionUID = 1L;

	public HttpServletRequest request;
	public JdbcManager jdbcManager;

	public Object invoke(final MethodInvocation invocation) throws Throwable {
		 // 対象のクラス
		String actonName = getTargetClass(invocation).getName().replaceAll("\\.", "/");

		// 対象のメソッド
		String methodName = invocation.getMethod().getName();

		TLog log = new TLog();

		log.crtdt = new Timestamp(System.currentTimeMillis());
		log.action = actonName +"/" + methodName;
		log.useragent = Utils.getAllHeader(request);
		log.body = Utils.getAllParameter(request);

		System.out.println(log.useragent);
		System.out.println(log.body);

		jdbcManager.insert(log).execute();

		return invocation.proceed();
	}
}

とするだけで、インターセプトしてくれました^^
requestにはインターセプト前のものが入っていました。
これでちょっとシンプルになった。後は例外時のインターセプトだ!