如何模拟一个方面

时间:2012-08-06 09:07:01

标签: java testing mocking aspectj

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

感谢。

5 个答案:

答案 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);
相关问题