Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to mock an aspect

I am currently working on some monitoring tool using aspectj. Because this tool should be technology independent (as far as possible), I am not using Spring for injection. But I want my aspects to be unit-tested.

Aspect example:

@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;
    }  
}

However, I really dont know, how to construct unit test (the repository field should be mocked (using mockito)), But I do not have aspect creation under control, hence I cannot set the dependency manually. What should I call to get the instance? Or there is some other scenario how to unit-test aspectj aspects.

Thanks.

like image 617
malejpavouk Avatar asked Aug 06 '12 09:08

malejpavouk


1 Answers

You say you find your own way of introducing the mock object hacky. What exactly do you dislike and how do you imagine it to be? I can only guess:

Do you dislike the fact that you globally replace calls to OwlApiRepository.getInstance() in your meta aspect? Then you could specifically restrict mock object injection to the aspect's constructor (I am using native AspectJ syntax because I feel uncomfortable with the POJO annotation style):

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

As you can also see, this variant of a meta (aspect-testing) aspect also has a switch to turn it on and off at will. Maybe this was also something you disliked. As I said, I am guessing. After your feedback I might be able to answer more specifically.

Edit: As for your concerns, I think I have addressed them as far as possible:

  • You do not need a mock holder.

  • The aspect can be (de)activated. It would be easy to make its activation dependent on other conditions, so it is only active in your test environment. If this is still not enough, use compile-time weaving for your production aspects and load-time weaving for your test aspect. This way its byte code will not even be present in your production environment.

  • My version does not globally replace anything, but like a good surgeon only cuts in a minimally invasive way exactly at one place.

  • I cannot really understand your concern about byte code manipulation for several reasons: You use AspectJ, i.e. inherently byte code manipulation (weaving). You use Mockito which creates classes at runtime. I also do not understand where you see a deficiency in AspectJ. You have not explained how you want the "standard means of the language" to behave or what interface it should provide for testing. Even if you had, I cannot change the language (AJ) and tool (Mockito) of your own choice for you.

like image 169
kriegaex Avatar answered Sep 20 '22 15:09

kriegaex