본문 바로가기
Homo Faber/Techniques

Spring의 AOP로 구현한 테스트 스파이(Spy)

by javauser 2009. 12. 4.

테스트 스파이는 테스트 더블(double) 의 한 유형으로, 대상 시스템 내의 특정 컴포넌트의 간접적인 결과 호출을 검증하기 위한 장치이다. 즉, 테스트 스파이는 해당 컴포넌트의 행위를 검증하기(behaviour verification) 위한 테스트 장치라고 볼 수 있다.
테스트 스파이는 다음과 같은 상황에서 사용된다.
  • 대상 시스템의 간접적인 결과를 검증하고자 하지만, 사전에 모든 속성의 값들을 예상할 수 없는 경우
  • Mock 객체를 사용해서 충분하게 예상을 하지 못한다고 생각할때 이를 보여주기 위한 검증을 필요한 경우
  • 동등성 비교와 같은 검증시 단정(assertion) 메소드를 사용해서 제어하지 못하는 경우

테스트 스파이에 대한 구현은 해당 로직을 대상 컴포넌트로 주입을 함으로써 비즈니스 로직 수행 중에 주입된 테스트 스파이를 수행하게 함으로써 해당 결과를 검증할 수 있다. 하지만, 이와 같은 방법으로 하는 경우, 결국 비즈니스 로직에 테스트 스파이가 주입되는 메소드나 속성이 만들어져야 한다. 따라서, 설계에 대한 조정이 필요한 작업이며, 대부분이 이러한 노력은 잘못하면 기존 로직에 손상을 입힐 수 있는 결과가 만들어질 수 있다.

이러한 방식의 테스트 스파이 주입을 Spring의 AOP를 사용한다면 어느정도 구현 로직과 테스트 구조를 달리해서 테스트 수행할 수 있으며, 기존 로직의 변경을 막을 수 있다.

Spring에서는 메소드의 실행 전후, 그리고, 예외 발생시에 해당 이벤트들을 capture하는 메커니즘을 제공한다.
이 인터페이스들은 org.springframework.aop.MethodBeforeAdvice, org.springframework.aop.AfterReturningAdvice, org.springframework.aop.ThrowsAdvice 이다. 이를 구현한 테스트 스파이 구현체를 만들고, 테스트 spring 설정 파일을 만들어서 이를 대상 컴포넌트에 proxy로 만들게끔 하고, 테스트 케이스에서 테스트를 수행하면 된다.

MethodBeforeAdvice는 메소드 수행 전에 호출되는 메소드를 가진 인터페이스로 그 메소드는 다음과 같다.

public void before(Method method, Object[] params, Object target) throws Throwable;

테스트 대상 컴포넌트의 메소드의 정보를 가진 method와 메소드가 가진 파라미터 정보인 params, 그리고 대상 컴포넌트의 인스턴스인 target을 파라미터로 가지고 있다.

AfterReturningAdvice는 메소드 수행 후에 호출되는 메소드를 가진 인터페이스로 그 메소드는 다음과 같다.

public void afterReturning(Object returnValue, Method method, Object[] params, Object target) throws Throwable;

이 메소드는 위의 before 메소드가 가진 파라미터 이외에 returnValue라는 메소드의 반환값을 가지고 있다.

마지막으로 예외 발생시에 사용되는 ThrowsAdvice 는 아무런 구현 메소드를 가지고 있지 않다. 이 인터페이스의 구현체에서는
public void afterThrowing(Throwable t) 라는 메소드를 구현하게 되면, 예외 발생시 이 메소드를 호출하게 된다.

테스트 스파이를 구현한 뒤에, 테스트 실행시 설정하는 spring XML 파일에서는 다음과 같이 선언한다.

<bean id="testSpy" class="com.company.test.spy.TesSpy"/>

<bean id="audienceAdvisor" class="org.springframework.aop.aspectj.AspectJExpressionPointcutAdvisor">
  <property name="advice" ref="testSpy" />
  <property name="expression" value="execution(* *.someMethodName(..))" />
</bean>

이상과 같이 AspectJ를 proxy로 사용하게끔 설정한 다음에 expression에서 대상이 되는 메소드를 설정하면 된다.

기존 테스트 케이스에서는 위의 설정이 포함된 spring XML을 같이 로딩하여 테스트를 수행하게 되면, 테스트 스파이를 통해 해당 메소드의 수행의 내용을 검증해볼 수 있다.

이와 같이 Spring의 AOP를 사용하게 되면, 메소드 행위에 대한 내용을 검증할 수는 있지만, 만일 해당 메소드의 로직이 길어질 경우, 그 내부를 검토하기란 쉽지 않다. 이 경우, 해당 메소드를 테스트하기 쉬운 단위로 나누어서 (private 메소드 형태) 이를 적용해볼 수 있다. 하지만, private 메소드를 구현하는 경우의 테스트는 꼭 Spring의 AOP를 사용하지 않더라도, reflection을 사용해서 테스트하는 방법도 있다.

결국, 테스트는 테스트를 수행하기 적당한 크기의 로직으로 나뉘어질 때 더 쉽고 간단한 방법을 통해 검증을 할 수 있게 된다.

반응형

'Homo Faber > Techniques' 카테고리의 다른 글

빌드 시간을 더 빠르게 하라  (0) 2012.05.12
신중하게 행동하라  (0) 2010.06.16
비즈니스 컴포넌트와 데이터 ownership  (4) 2009.10.05
m2eclipse 설치  (0) 2009.09.22
m2eclipse 소개  (0) 2009.09.22