プロフィール

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

FC2カウンター


上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。
  • 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;
}

}





















コメント

コメントの投稿

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