我目前正在使用aspectj开发一些监控工具。因为这个工具应该是技术独立的(尽可能),所以我没有使用Spring进行注射。但我希望我的方面能够经过单元测试。
方面示例:
@Aspect
public class ClassLoadAspect {
private Repository repository;
public ClassLoadAspect() {
repository = OwlApiRepository.getInstance();
}
@After("anyStaticInitialization()")
public void processStaticInitilization(JoinPoint jp) {
Class type = jp.getSourceLocation().getWithinType();
if (type.isInterface()) {
repository.storeInterfaceInitialization(type);
} else if (type.isEnum()) {
repository.storeEnumInitialization(type);
} else {
repository.storeClassInitialization(type);
}
}
@Pointcut("staticinitialization(*) && !within(cz.cvut.kbss.odra..*)")
public void anyStaticInitialization() {
}
public Repository getRepository() {
return repository;
}
public void setRepository(Repository repository) {
this.repository = repository;
}
}
但是,我真的不知道,如何构建单元测试(存储库字段应该被模拟(使用mockito)),但是我没有控制方面创建,因此我不能手动设置依赖项。我该怎么称呼才能获得实例?或者还有一些其他场景如何对aspectj方面进行单元测试。
感谢。
答案 0 :(得分:2)
你说你找到了自己介绍模拟对象hacky的方法。你究竟不喜欢什么,你怎么想象它?我只能猜测:
您是否不喜欢全局替换元数据中对OwlApiRepository.getInstance()
的调用?然后你可以专门限制模拟对象注入到方面的构造函数(我使用原生的AspectJ语法,因为我对POJO注释样式感到不舒服):
public privileged aspect ClassLoadTestAspect {
static boolean active = true;
declare precedence : ClassLoadTestAspect, ClassLoadAspect;
pointcut classLoadAspect() :
if(active) &&
withincode(ClassLoadAspect.new()) &&
call(* OwlApiRepository.getInstance());
Object around() : classLoadAspect() {
return new MockRepository();
}
}
正如您所看到的,元(方面测试)方面的这种变体也有一个开关可以随意打开和关闭它。也许这也是你不喜欢的事情。正如我所说,我猜。在您的反馈之后,我可以更具体地回答。
编辑:关于您的疑虑,我想我已尽可能地解决了这些问题:
您不需要模拟持有人。
可以(de)激活方面。根据其他条件进行激活很容易,因此它只在您的测试环境中有效。如果仍然不够,请为您的生产方面使用编译时编织,并为您的测试方面使用加载时编织。这样,它的字节代码甚至不会出现在您的生产环境中。
我的版本并不能全局取代任何东西,但像一位优秀的外科医生只能在一个地方完全切入微创方式。
由于以下几个原因,我无法理解您对字节码操作的担忧:使用AspectJ,即固有的字节码操作(编织)。您使用Mockito在运行时创建类。我也不明白你在哪里看到AspectJ的缺陷。您还没有解释您希望“语言的标准方法”如何表现或者应该为测试提供什么接口。即使你有,我也不能为你自己选择语言(AJ)和工具(Mockito)。
答案 1 :(得分:1)
你可以拆分你的测试。首先测试方面的逻辑。这是一个pojo。你可以随心所欲地测试它。第二部分是测试切入点。在这种情况下,使用相同的切入点创建另一个简单的方面(例如,将它们提取为常量)。也许有一些专门的测试工具,但我不知道任何,这是我想到的最简单的方法
答案 2 :(得分:1)
我目前的解决方案是引入这个AspectJ hack以覆盖单例工厂方法
@Aspect
public class MockingAspect {
@Around("call(synchronized static OwlApiRepository *(..))")
public OwlApiRepository processGetInstance(ProceedingJoinPoint jp) {
System.out.println("getting mock");
return MockHolder.getMock();
}
}
答案 3 :(得分:1)
您只是想进行单元测试吗? 这是一个小单元测试,用于测试带有方面的自定义注释,目的是将throwable包装在自定义应用程序异常中。 (Testng + Mockito)
public class ResourceApplicationExceptionAspectTest {
@Mock
private ProceedingJoinPoint pjp;
@Mock
private ResourceApplicationException resourceApplicationException; //annotation definition
@BeforeMethod
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
}
@Test(groups ="unit", expectedExceptions = ResourceApplicationException.class)
public void testWrapExceptionAdvice() throws Throwable {
ResourceApplicationExceptionAspect aspect = new ResourceApplicationExceptionAspect();
when(pjp.proceed()).thenThrow(new NullPointerException());
aspect.wrapExceptionAdvice(pjp, resourceApplicationException);
}
答案 4 :(得分:0)
这些方面的内容如何,基本上继续保持你的方面,内部方面将行为委托给另一个接口,并模拟测试的接口,而不是模拟方面本身。这是一个伪代码:
public interface ClassLoadHelper{
void processStaticInitialization(Class<?> clazz);
}
public class ClassLoadHelperImpl implements ClassLoadHelper{
private Repository repository;
public ClassLoadHelperImpl() {
repository = OwlApiRepository.getInstance();
}
void processStaticInitialization(Class<?> clazz){
if (type.isInterface()) {
this.repository.storeInterfaceInitialization(type);
} else if (type.isEnum()) {
this.repository.storeEnumInitialization(type);
} else {
this.repository.storeClassInitialization(type);
}
}
}
@Aspect
public class ClassLoadAspect {
private ClassLoadHelper classLoadHelper;
@After("anyStaticInitialization()")
public void processStaticInitilization(JoinPoint jp) {
Class<?> type = jp.getSourceLocation().getWithinType();
this.classLoadHelper.processStaticInitialization(type);
}
@Pointcut("staticinitialization(*) && !within(cz.cvut.kbss.odra..*)")
public void anyStaticInitialization() {
}
public ClassLoadHelper getClassLoadHelper() {
return classLoadHelper;
}
public void setClassLoadHelper(ClassLoadHelper classLoadHelper) {
this.classLoadHelper = classLoadHelper;
}
}
现在,您可以在测试中执行此操作:
ClassLoadAspect.aspectOf().setClassLoadHelper(mockClassLoadHelper);