Java 如何模仿一个方面
我目前正在使用aspectj开发一些监控工具。因为这个工具应该是技术独立的(尽可能),所以我不使用Spring进行注射。但我想让我的方面进行单元测试 方面示例:Java 如何模仿一个方面,java,testing,mocking,aspectj,Java,Testing,Mocking,Aspectj,我目前正在使用aspectj开发一些监控工具。因为这个工具应该是技术独立的(尽可能),所以我不使用Spring进行注射。但我想让我的方面进行单元测试 方面示例: @Aspect public class ClassLoadAspect { private Repository repository; public ClassLoadAspect() { repository = OwlApiRepository.getInstance(); }
@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;
}
}
然而,我真的不知道如何构造单元测试(repository字段应该被模拟(使用mockito)),但是我没有控制方面的创建,因此我无法手动设置依赖项。我应该调用什么来获取实例?或者还有其他一些场景如何对aspectj方面进行单元测试
谢谢。您可以拆分测试。首先测试方面的逻辑。这是一个pojo。你可以随意测试它。第二部分是测试切入点。在这种情况下,创建另一个具有相同切入点的简单方面(例如,将它们提取为常量)。也许有一些专用的测试工具,但我不知道有没有,这是我想到的最简单的方法我当前的解决方案是引入AspectJ hack,以覆盖Singleton factory方法
@Aspect
public class MockingAspect {
@Around("call(synchronized static OwlApiRepository *(..))")
public OwlApiRepository processGetInstance(ProceedingJoinPoint jp) {
System.out.println("getting mock");
return MockHolder.getMock();
}
}
沿着这些思路,基本上继续保留您的方面,在方面内部将行为委托给另一个接口,并为您的测试模拟该接口,而不是模拟方面本身。下面是一个伪代码:
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;
}
}
你说你找到了自己的方法来引入模拟对象hacky。你到底不喜欢什么?你怎么想?我只能猜测: 您是否不喜欢在元方面全局替换对
OwlApiRepository.getInstance()
的调用?然后,您可以专门将模拟对象注入限制在方面的构造函数中(我使用本机AspectJ语法,因为我对POJO注释样式感到不舒服):
您还可以看到,meta(方面测试)方面的这个变体也有一个开关,可以随意打开和关闭它。也许这也是你不喜欢的。正如我所说,我在猜测。在您的反馈之后,我可能能够更具体地回答
编辑:关于您的担忧,我想我已经尽可能地解决了:
- 您不需要模拟支架
- 该特性可以(取消)激活。它很容易使其激活依赖于其他条件,因此它仅在您的测试环境中处于活动状态。如果这还不够,请对生产方面使用编译时编织,对测试方面使用加载时编织。这样,它的字节码甚至不会出现在您的生产环境中
- 我的版本不会在全球范围内取代任何东西,但就像一个好的外科医生一样,只会在一个地方进行微创切割
- 我无法真正理解您对字节码操纵的担忧,原因有几个:您使用AspectJ,即固有的字节码操纵(编织)。您可以使用Mockito,它在运行时创建类。我也不明白您在哪里看到AspectJ的缺陷。您没有解释您希望“语言的标准方法”如何运行,或者它应该为测试提供什么接口。即使你有,我也不能为你改变你自己选择的语言(AJ)和工具(Mockito)
在我的解决方案中我不喜欢的是:静态方法的全局替换使得测试变得困难——我必须手动重置存储库模拟(而不是设置一个新的)。我还必须引入一个mock持有者来访问mock repository对象。第三件事是,我不喜欢为了设置mock而对字节码进行调整,我真的认为这必须通过语言的标准方式来完成(如果不可能,那么它显示了aspectj设计中的缺陷)。但从代码来看,您的解决方案可能会(至少不需要持有者):-)。重点是,赏金是您的。谢谢:-)对不起,太吵了。我决定在回答中添加注释,因为可用于注释的字符数太少。不过还是要谢谢你赏金给我
ClassLoadAspect.aspectOf().setClassLoadHelper(mockClassLoadHelper);
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();
}
}
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);
}