我正在尝试为Custom Aspect编写Junit测试。这是Aspect Class Snippet:
@Aspect
@Component
public class SampleAspect {
private static Logger log = LoggerFactory.getLogger(SampleAspect.class);
@Around("execution(* org.springframework.data.mongodb.core.MongoOperations.*(..)) || execution(* org.springframework.web.client.RestOperations.*(..))")
public Object intercept(final ProceedingJoinPoint point) throws Throwable {
logger.info("invoked Cutom aspect");
return point.proceed();
}
}
因此,只要关节点与切入点匹配,上述方面就会截获。工作正常。
但我的问题是如何对该课进行单元测试。我有以下Junit测试:
@Test(expected = MongoTimeoutException.class)
public void TestWithMongoTemplate() {
//MongoDocument class
TestDocument test = new TestDocument();
ApplicationContext ctx = new AnnotationConfigApplicationContext(TestMongoConfigurationMain.class);
MongoTemplate mongoTemplate = ctx.getBean(MongoTemplate.class);
//this call is being intercepted by SampleAspect
mongoTemplate.save(test);
}
所以Junit中的mongoTemplate.save(test)
被SampleAspect
拦截,因为它与切入点匹配。但是,我应该如何确保在调用该关节点时我的SampleAspect
正在拦截junits(可能是断言)?
我无法断言来自intercept()
的返回值,因为除了执行关节点之外它没有什么特别之处。因此,我的Junit无论是基于返回值还是按方面执行还是常规执行都无法找到任何区别。
如果提供方面测试的任何代码片段示例都会很棒。谢谢
答案 0 :(得分:13)
我认为你要测试的是方面编织和切入点匹配。请注意,这将是一个集成而不是单元测试。如果你真的想要对你的方面逻辑进行单元测试,并且因为你已经通过" mockito"标记了问题。无论如何,我建议你这样做:写一个单元测试并模拟方面的连接点,也许还有其他参数,如果有的话。这是一个稍微复杂的示例,其中包含一些内部逻辑:
以方面为目标的Java类:
package de.scrum_master.app;
public class Application {
public static void main(String[] args) {
new Application().doSomething(11);
new Application().doSomething(-22);
new Application().doSomething(333);
}
public void doSomething(int number) {
System.out.println("Doing something with number " + number);
}
}
受测试的方面:
package de.scrum_master.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
@Aspect
public class SampleAspect {
@Around("execution(* doSomething(int)) && args(number)")
public Object intercept(final ProceedingJoinPoint thisJoinPoint, int number) throws Throwable {
System.out.println(thisJoinPoint + " -> " + number);
if (number < 0)
return thisJoinPoint.proceed(new Object[] { -number });
if (number > 99)
throw new RuntimeException("oops");
return thisJoinPoint.proceed();
}
}
运行Application.main(..)
时的控制台日志:
正如你所看到的,方面传递11,否定-22并抛出333的异常:
execution(void de.scrum_master.app.Application.doSomething(int)) -> 11
Doing something with number 11
execution(void de.scrum_master.app.Application.doSomething(int)) -> -22
Doing something with number 22
execution(void de.scrum_master.app.Application.doSomething(int)) -> 333
Exception in thread "main" java.lang.RuntimeException: oops
at de.scrum_master.aspect.SampleAspect.intercept(SampleAspect.aj:15)
at de.scrum_master.app.Application.doSomething(Application.java:10)
at de.scrum_master.app.Application.main(Application.java:7)
方面的单元测试:
现在我们真的想要验证方面是否应该完成它并覆盖所有执行路径:
package de.scrum_master.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.junit.Rule;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import static org.mockito.Mockito.*;
public class SampleAspectTest {
@Rule
public MockitoRule mockitoRule = MockitoJUnit.rule();
@Mock
private ProceedingJoinPoint proceedingJoinPoint;
private SampleAspect sampleAspect = new SampleAspect();
@Test
public void testPositiveSmallNumber() throws Throwable {
sampleAspect.intercept(proceedingJoinPoint, 11);
// 'proceed()' is called exactly once
verify(proceedingJoinPoint, times(1)).proceed();
// 'proceed(Object[])' is never called
verify(proceedingJoinPoint, never()).proceed(null);
}
@Test
public void testNegativeNumber() throws Throwable {
sampleAspect.intercept(proceedingJoinPoint, -22);
// 'proceed()' is never called
verify(proceedingJoinPoint, never()).proceed();
// 'proceed(Object[])' is called exactly once
verify(proceedingJoinPoint, times(1)).proceed(new Object[] { 22 });
}
@Test(expected = RuntimeException.class)
public void testPositiveLargeNumber() throws Throwable {
sampleAspect.intercept(proceedingJoinPoint, 333);
}
}
现在运行这个简单的JUnit + Mockito测试,以便隔离测试方面逻辑,不布线/编织逻辑。对于后者,您需要另一种类型的测试。
P.S。:仅限你使用JUnit和Mockito。通常我只使用Spock及其内置的模拟功能。 ;-)