プロフィール

Author:はるかわ しおん
なにか、新しいものを。
そして、楽しいものを。

FC2カウンター


上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。
  • Spring でセッションスコープを使い、JUnit 単体テスト

Spring2.0 を使っているが、どうもセッション周りの情報がない。
Bean のスコープに request/session が追加されたというが、具体的にそれをどう使えばよいのかよくわからん。

SpringMVC からでないと使えないのか、Spring と連携した Struts からでもいいのか、ピュアな JSP-Servlet だけの場合はやっぱり使えないのか。そのへんがね・・・。

さしあたり、SpringMVC を使って構築したサンプルアプリにおいて、コントローラでセッションを利用してみた。
この記事は、それを JUnit テストしたときの (自分向けの!) 覚書です。

少なくとも、これで意図することはできたのだが、正しいやり方なのかどうかよくわからんので注意。

※このアプリについて説明していませんが、前々画面でアカウント名をセッションに保持し、前画面でデータベース検索用の各種値を投げてきているだけのものです。私はわかっているので、詳細は割愛。




・コントローラクラス

package jp.co.example.presentation.controller;

import java.util.List;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import jp.co.example.logic.Bumon;
import jp.co.example.logic.BumonService;
import jp.co.example.logic.LoginInfo;
import jp.co.example.presentation.command.AddAndGetAllCommand;
import jp.co.example.util.Exception;

import org.springframework.validation.BindException;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.SimpleFormController;



/**
* コントローラ
*/
public class AddAndGetAllController extends SimpleFormController {

private LoginInfo loginInfo = null;

private BumonService bumonService = null;

public void setBumonService(BumonService bumonService) {
this.bumonService = bumonService;
}

public void setLoginInfo(LoginInfo loginInfo) {
this.loginInfo = loginInfo;
}

/**
* サブミットボタンが押されたときの処理
*/
protected ModelAndView onSubmit(HttpServletRequest request,
HttpServletResponse response, Object command, BindException bind)
throws Exception {

try {
System.out.println("LoginInfo@AddAndGetAllController: " + loginInfo.getAccount()); // これでいいのかな??? (loginInfo を DI させてる)

if (loginInfo.getAccount().equals("")) {
throw new Exception("アカウントがない!!!");
}
} catch (Exception e) {
e.printStackTrace();

throw new Exception("なんかおかしい:" + e.toString());

}

// 入力クラスをキャストし、メッセージを取り出す
AddAndGetAllCommand addAndGetAllCommand = (AddAndGetAllCommand)command;


// データベース検索
Bumon bumon = new Bumon(); // 部門コード
bumon.setDeptCd(Integer.parseInt(addAndGetAllCommand.getDeptCd()));
bumon.setDeptName(addAndGetAllCommand.getDeptName());

List resultBumonList = bumonService.addAndGetAll(bumon); // 検索


// データ受け渡し・画面遷移
ModelAndView mav = new ModelAndView(getSuccessView(), bind.getModel());
mav.addObject("bumonList", resultBumonList);

return mav;

}

}




・コマンドクラス (アカウント名を受け取る)

package jp.co.example.logic;

public class LoginInfo {

private String account;

public String getAccount() {
return account;
}

public void setAccount(String account) {
this.account = account;
}


}




・Bean定義ファイル SpringMVCDeptest-servlet.xml


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">

<!-- Spring ビュー名からJSPファイルを紐付けるクラス -->
<bean id="viewResolver"
class=
"org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>

<!-- DataSource -->
<bean id="dataSource"
class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<property name="driverClassName" value="${db.driver}" />
<property name="url" value="${db.url}" />
<property name="username" value="${db.username}" />
<property name="password" value="${db.password}" />
</bean>


<!-- PropertyPlaceholder -->
<bean
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="/WEB-INF/classes/database_Pgsql.properties" /> <!-- PostgreSQL用 -->
</bean>


<!-- dao/DAO -->
<bean id="bumonDao" class="jp.co.example.dao.BumonDaoImplPgsql"> <!-- PostgreSQL用 -->
<property name="dataSource" ref="dataSource" />
</bean>


<!-- logic/Service -->
<bean id="bumonService" class="jp.co.example.logic.BumonServiceImpl">
<property name="bumonDao" ref="bumonDao" />
</bean>


<!-- セッション保持オブジェクト -->
<bean id="loginInfo" class="jp.co.example.logic.LoginInfo" scope="session" >
<!-- <aop:scoped-proxy /> -->
</bean>


<!-- コントローラ -->
<bean name="/login.form"
class="jp.co.example.presentation.controller.LoginController" scope="session">
<property name="formView" value="index" />
<property name="successView" value="menu" />
<property name="commandClass" value="jp.co.example.presentation.command.LoginCommand"/>
<property name="commandName" value="loginCommand"/>
<property name="loginInfo" ref="loginInfo"/>
</bean>

<bean name="/get.form"
class="jp.co.example.presentation.controller.GetController" scope="session">
<property name="formView" value="menu" />
<property name="successView" value="get" />
<property name="commandClass" value="jp.co.example.presentation.command.GetCommand"/>
<property name="commandName" value="getCommand"/>
<property name="bumonService" ref="bumonService"/>
<property name="loginInfo" ref="loginInfo"/>
</bean>

<bean name="/addAndGetAll.form"
class="jp.co.example.presentation.controller.AddAndGetAllController" scope="session">
<property name="formView" value="menu" />
<property name="successView" value="addAndGetAll" />
<property name="commandClass" value="jp.co.example.presentation.command.AddAndGetAllCommand"/>
<property name="commandName" value="addAndGetAllCommand"/>
<property name="bumonService" ref="bumonService"/>
<property name="loginInfo" ref="loginInfo"/>
</bean>


</beans>




・テストクラス

package jp.co.example.presentation.controller;

import java.util.List;

import jp.co.example.logic.Bumon;
import jp.co.example.logic.LoginInfo;

import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.test.AbstractDependencyInjectionSpringContextTests;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.context.request.SessionScope;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;

/**
* あとから気づいたが、
* http://www.pwv.co.jp/~take/TakeWiki/index.php?Spring-MVC%E3%81%A7spring%E3%81%AEsession%20scope%E3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E3%82%92%E3%83%86%E3%82%B9%E3%83%88%E3%81%97%E3%81%9F%E3%81%84
* もすごくよさそうである。要確認。
*
*/
public class AddAndGetAllControllerTest extends
AbstractDependencyInjectionSpringContextTests {

private ServletRequestAttributes attributes;

private Controller controller;

@Override
protected String[] getConfigLocations() {
// scope.xml has one bean with scope="session"
return new String[] { "jp/co/example/presentation/controller/SpringMVCDeptest-servlet_testsession.xml" };
}

private void setUpRequest(ConfigurableApplicationContext context) {
context.getBeanFactory().registerScope("session", new SessionScope());
MockHttpServletRequest request = new MockHttpServletRequest();
attributes = new ServletRequestAttributes(request);
RequestContextHolder.setRequestAttributes(attributes);
}

@Override
protected void prepareTestInstance() throws Exception {
setUpRequest(applicationContext);
super.prepareTestInstance();
}

public void testOnSubmit() {

// セッションの用意・・・も Spring にまかせる感じで動いたが、これでいいのかは不明。ぐぐっても情報なし。でも、考えてみれば、「Spring にまかせる」とはこういうことか?
LoginInfo loginInfo = (LoginInfo)this.getApplicationContext().getBean("loginInfo");
loginInfo.setAccount("LOGIN ACCOUNT");


controller = (Controller)this.getApplicationContext().getBean("/addAndGetAll.form");

MockHttpServletRequest req = new MockHttpServletRequest("POST", "addAndGetAll.form");
req.addParameter("deptCd", "0");
req.addParameter("deptName", "モックでテキトーに返してくるので、ここの値はなんでもいい");

ModelAndView mv = null;
try {
mv = controller.handleRequest(req,
new MockHttpServletResponse());
System.out.println("VIEW: " + mv.getViewName());
} catch (Exception err) {
err.printStackTrace();
fail();
}

// 遷移先のチェック
assertEquals("addAndGetAll", mv.getViewName());

// 受け渡しオブジェクトのチェック
List resultBumonList =(List)mv.getModel().get("bumonList");
assertEquals(resultBumonList.size(), 2);
assertEquals(resultBumonList.get(0).getDeptCd(), 1);
assertEquals(resultBumonList.get(0).getDeptName(), "モックで追加しました1");
assertEquals(resultBumonList.get(1).getDeptCd(), 2);
assertEquals(resultBumonList.get(1).getDeptName(), "モックで追加しました2");


}

}
スポンサーサイト
  • Spring で AOP するためのクラスの単体テスト

番外編 Spring AOPによるロギングの実装を元に作成したクラスを JUnit します。
※ソースはこれ

ここで悩んだのが、JoinPoint/ProceedingJoinPointMock について。
djUnit の VirtualMockObjects を使う・・・のも案外大変 (interface だから!) なので、今回は interface を実装したモッククラスを利用することにしました。

下記は、あくまで私の覚書ですが、ソースをドバーっと記載。



・JoinPoint用のモック

package jp.co.example.aspect.mock;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.reflect.SourceLocation;

public class JoinPointMock implements JoinPoint {

@Override
public Object[] getArgs() {
// TODO 自動生成されたメソッド・スタブ
return null;
}

@Override
public String getKind() {
// TODO 自動生成されたメソッド・スタブ
return null;
}

@Override
public Signature getSignature() {
return new SignatureMock();
}

@Override
public SourceLocation getSourceLocation() {
// TODO 自動生成されたメソッド・スタブ
return null;
}

@Override
public StaticPart getStaticPart() {
// TODO 自動生成されたメソッド・スタブ
return null;
}

@Override
public Object getTarget() {
return this;
}

@Override
public Object getThis() {
// TODO 自動生成されたメソッド・スタブ
return null;
}

@Override
public String toLongString() {
// TODO 自動生成されたメソッド・スタブ
return null;
}

@Override
public String toShortString() {
// TODO 自動生成されたメソッド・スタブ
return null;
}

}




・ProceedingJoinPoint用モック

package jp.co.example.aspect.mock;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.reflect.SourceLocation;
import org.aspectj.runtime.internal.AroundClosure;

public class ProceedingJoinPointMock implements ProceedingJoinPoint {

private Object returnObj;

public Object getReturnObj() {
return returnObj;
}

public void setReturnObj(Object returnObj) {
this.returnObj = returnObj;
}

@Override
public Object proceed() throws Throwable {
return returnObj;
}

@Override
public Object proceed(Object[] arg0) throws Throwable {
// TODO 自動生成されたメソッド・スタブ
return null;
}

@Override
public void set$AroundClosure(AroundClosure arg0) {
// TODO 自動生成されたメソッド・スタブ

}

@Override
public Object[] getArgs() {
Object args[] = {"ARG1", "ARG2"};

return args;
}

@Override
public String getKind() {
// TODO 自動生成されたメソッド・スタブ
return null;
}

@Override
public Signature getSignature() {
return new SignatureMock();
}

@Override
public SourceLocation getSourceLocation() {
// TODO 自動生成されたメソッド・スタブ
return null;
}

@Override
public StaticPart getStaticPart() {
// TODO 自動生成されたメソッド・スタブ
return null;
}

@Override
public Object getTarget() {
return this;
}

@Override
public Object getThis() {
// TODO 自動生成されたメソッド・スタブ
return null;
}

@Override
public String toLongString() {
// TODO 自動生成されたメソッド・スタブ
return null;
}

@Override
public String toShortString() {
// TODO 自動生成されたメソッド・スタブ
return null;
}

}




・Signature用のモック

package jp.co.example.aspect.mock;

import org.aspectj.lang.Signature;

public class SignatureMock implements Signature {

@Override
public Class getDeclaringType() {
// TODO 自動生成されたメソッド・スタブ
return null;
}

@Override
public String getDeclaringTypeName() {
// TODO 自動生成されたメソッド・スタブ
return null;
}

@Override
public int getModifiers() {
// TODO 自動生成されたメソッド・スタブ
return 0;
}

@Override
public String getName() {
// TODO 自動生成されたメソッド・スタブ
return "NAME";
}

@Override
public String toLongString() {
// TODO 自動生成されたメソッド・スタブ
return null;
}

@Override
public String toShortString() {
// TODO 自動生成されたメソッド・スタブ
return null;
}

}




・テストケース
package jp.co.example.aspect;

import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.lang.reflect.Method;

import jp.co.example.aspect.mock.JoinPointMock;
import jp.co.example.aspect.mock.ProceedingJoinPointMock;
import junit.framework.TestCase;

import org.aspectj.lang.JoinPoint;

public class LoggingAspectTest extends TestCase {

LoggingAspect la = new LoggingAspect();

/**
* {@link jp.co.example.aspect.LoggingAspect#logAroundExecution(org.aspectj.lang.ProceedingJoinPoint)} のためのテスト・メソッド。
*/
public void testLogAroundExecution() {


try {

// 実行
ProceedingJoinPointMock pjp = new ProceedingJoinPointMock();
pjp.setReturnObj("RETURN");

assertEquals("RETURN", la.logAroundExecution(pjp));

} catch (Throwable e) {
e.printStackTrace();
fail();
}


}


/**
* private なメソッドは、リフレクションで試験。
*/
public void testCreateSB() {

try {
// 対象メソッドの取り出し
Class c = LoggingAspect.class;

Class argTypes[] = {String.class, String.class};

Method method = c.getDeclaredMethod("createSB", argTypes);


// アクセス許可
method.setAccessible(true);


// 実行
Object args[] = {"a", "b"};

StringBuilder result = (StringBuilder)method.invoke(la, args);
assertEquals("a b()", result.toString());

} catch (Exception e) {

e.printStackTrace();
fail();

}

}


/**
* 外部のクラスに依存するようなテストは、djUnit の VirtualMockObjects を使う・・・のも案外大変 (interface だから!) なので、今回は interface を実装したモッククラスを利用。
*/
public void testGetLogMessage() {

try {
// 対象メソッドの取り出し
Class c = LoggingAspect.class;

Class argTypes[] = {JoinPoint.class, String.class};

Method method = c.getDeclaredMethod("getLogMessage", argTypes);


// アクセス許可
method.setAccessible(true);


// 実行
JoinPoint jp = new JoinPointMock();

Object args[] = {jp, "a"};

String result = (String)method.invoke(la, args);
assertEquals("jp.co.example.aspect.mock.JoinPointMock NAME a", result.toString());

} catch (Exception e) {

e.printStackTrace();
fail();

}

}



}




・テスト対象のロギングクラス

package jp.co.example.aspect;

import java.io.PrintWriter;
import java.io.StringWriter;

import org.apache.log4j.Logger;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;

public class LoggingAspect {

private static Logger logger = Logger.getLogger(LoggingAspect.class);

private String getLogMessage(JoinPoint jp, String msg) {
String cName = jp.getTarget().getClass().getName(); // クラス名
String mName = jp.getSignature().getName(); // メソッド名
return cName + " " + mName + " " + msg;
}

/**
* serviceパッケージに属するクラスのpublicメソッド呼び出し前後 (例外発生も含む) にログを出力する。
*


* クラス名 メソッド名 START [パラメータ1, パラメータ2, ...]
* クラス名 メソッド名 END 実行時間(ms) [戻り値]
*/
public Object logAroundExecution(ProceedingJoinPoint pjp) throws Throwable {
String cName = pjp.getTarget().getClass().getName();
String mName = pjp.getSignature().getName();
Object[] args = pjp.getArgs();

// 呼び出し前ログ出力
StringBuilder beforeSB = createSB(cName, mName);
beforeSB.append(" START ");
for (Object arg : args) {
beforeSB.append(arg);
beforeSB.append(", ");
}
if (beforeSB.lastIndexOf(", ") == beforeSB.length() - 2) {
beforeSB.delete(beforeSB.length() - 2, beforeSB.length());
}

logger.debug(beforeSB.toString());

long beforeTime = System.currentTimeMillis();

Object retVal = null;
try {
retVal = pjp.proceed();
} catch (Exception e) {
// TODO このままだと、親クラスでもこのメッセージを出してしまう。対策は『Spring2.0 入門』P.73 参照。
logger.error("例外発生: " + e.getClass().getName());

StringWriter writer = new StringWriter();
PrintWriter writer2 = new PrintWriter(writer);
e.printStackTrace(writer2);
writer2.flush();
String stackTrace = writer.toString();
logger.error(stackTrace);

throw e;
}

long afterTime = System.currentTimeMillis();
long execTime = afterTime - beforeTime;

// 呼び出し後ログ出力
StringBuilder afterSB = createSB(cName, mName);
afterSB.append(" END ");
afterSB.append(execTime);
afterSB.append("(ms) ");
if (retVal != null) {
afterSB.append(retVal);
}

logger.debug(afterSB.toString());

return retVal;
}

private StringBuilder createSB(String cName, String mName) {
StringBuilder sb = new StringBuilder();
sb.append(cName);
sb.append(" ");
sb.append(mName);
sb.append("()");
return sb;
}

}


















ドラゴンクエスト9です。
ガングロとか賛否両論あるかとは思いますが、それはさておき。

なんとかレベルアップを自動で(寝ているときに)やりたいので、Aボタンを自動で連打する装置(?)を作りました。

CIMG1471.jpg

CIMG1472.jpg

ヤマダ電機で500円くらいだった電動歯ブラシの先っぽに消しゴムをムリヤリつけて、電源オン!
すると、なんとAボタンが連打されます!!!

・・・まぁ、世の中にはこんなアナログな手段じゃなくて、電子的にやっちゃうすごい方もいらっしゃるようですが、さしあたりこれでしばらくやっていこうと思っています。
  • 学習 - djUnit 及び ちゃんとしたテストケースの書き方

djUnit の勉強のメモ。

http://works.dgic.co.jp/djwiki/Viewpage.do?pid=@646A556E6974


djUnit を使うときのテストケースの書き方のメモ。

http://works.dgic.co.jp/djwiki/Viewpage.do?pid=@48656C6C6F576F726C64E381AEE38386E382B9E38388
Spring の学習に使ったサイトをメモ。

http://www.okisoft.co.jp/esc/spring/spring01.html
  • JavaDoc いちらん

いつも困るので、JavaDoc のいちらんを作成。

















Ver/種類SEEE備考
1.6JavaTM Platform, Standard Edition 6
API 仕様
JavaTM Platform, Enterprise Edition 6
API Specification
1.5JavaTM 2 Platform Standard Edition 5.0
API 仕様
JavaTM Platform Enterprise Edition, v 5.0
API Specifications
1.4JavaTM 2 Platform, Standard Edition, 1.4.0
API 仕様
JavaTM 2 Platform Enterprise Edition, v 1.4
API Specification
1.3JavaTM Platform, Standard Edition, v 1.3
API 仕様
JavaTM 2 Platform, Enterprise Edition, v 1.3.1
API 仕様

  • T-2 シミュレータ @浜松エアーパーク

浜松は航空自衛隊のエアーパークで、ホンモノの T-2 シミュレータを操縦してきました。

ぐぐってみると、酔ったという記事を書いている方もいたのでドキドキしていたのですが、
例えばディズニーランドのスターツアーズのように揺れるわけではなく、(電車でも酔うようになってしまった私でも) まったく大丈夫でした。

CIMG5120.jpg

私は上級をやったのですが、敵戦闘機をかなり撃墜したものの、敵機は無限に出てくるのかな?

個人的には、離陸時にスロットルをアフターバーナー位置にしたときに G を感じられたことと、操縦桿が足の間にあること、エースコンバットなどとは違ってフラップ下げたり「足で」ラダーを操作しつつ着陸していく (難しい!) のが本当に良かったです。

また行きたい!


上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。