5

我目前正在使用 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方面进行单元测试。

谢谢。

4

5 回答 5

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();
    }
}

您还可以看到,元(方面测试)方面的这个变体也有一个开关,可以随意打开和关闭它。也许这也是你不喜欢的东西。正如我所说,我在猜测。在您的反馈之后,我可能能够更具体地回答。

编辑:至于您的担忧,我想我已经尽可能地解决了它们:

  • 您不需要模拟支架。

  • 该方面可以(去)激活。使其激活依赖于其他条件很容易,因此它仅在您的测试环境中有效。如果这还不够,请在生产方面使用编译时编织,在测试方面使用加载时编织。这样,它的字节码甚至不会出现在您的生产环境中。

  • 我的版本不会在全球范围内取代任何东西,但就像一位优秀的外科医生只在一个地方以微创方式进行切割。

  • 由于以下几个原因,我无法真正理解您对字节码操作的担忧:您使用 AspectJ,即固有的字节码操作(编织)。您使用在运行时创建类的 Mockito。我也不明白你在哪里看到 AspectJ 的不足。您还没有解释您希望“语言的标准方式”如何表现或它应该为测试提供什么接口。即使您有,我也无法为您更改您自己选择的语言(AJ)和工具(Mockito)。

于 2012-08-10T17:18:19.663 回答
1

你可以拆分你的测试。首先测试方面的逻辑。这是一个pojo。你可以随心所欲地测试它。第二部分是测试切入点。在这种情况下,创建另一个具有相同切入点的简单方面(例如,将它们提取为常量)。也许有一些专用的测试工具,但我不知道,这是我想到的最简单的方法

于 2012-08-06T09:12:36.303 回答
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();
    }
}
于 2012-08-06T14:23:25.897 回答
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);
}
于 2013-03-26T11:08:29.083 回答
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);
于 2012-08-08T15:41:04.773 回答