AspectJ 的 JUnit 测试

发布于 2025-01-16 07:48:15 字数 1381 浏览 2 评论 0原文

我正在尝试为自定义方面编写 Junit 测试。这是 Aspect 类片段:

@Aspect
@Component
public class SampleAspect {

    private static Logger log = LoggerFactory.getLogger(SampleAspect.class);

    @Around("execution(* org.springframework.data.mongodb.core.MongoOperations.*(..)) || execution(* org.springframework.web.client.RestOperations.*(..))")
    public Object intercept(final ProceedingJoinPoint point) throws Throwable {
        logger.info("invoked Cutom aspect");
         return point.proceed();

    }

}

因此,每当 joinpoint 与切入点匹配时,上述方面就会进行拦截。它工作正常。

但我的问题是如何对该类进行单元测试。我有以下 Junit 测试:

@Test(expected = MongoTimeoutException.class)
    public void TestWithMongoTemplate() {
        //MongoDocument class
        TestDocument test = new TestDocument();

        ApplicationContext ctx = new AnnotationConfigApplicationContext(TestMongoConfigurationMain.class);
        MongoTemplate mongoTemplate = ctx.getBean(MongoTemplate.class);

        //this call is being intercepted by SampleAspect
        mongoTemplate.save(test);

    }

因此,Junit 中的 mongoTemplate.save(test)SampleAspect 拦截,因为它与切入点匹配。但是我应该如何在 junits 中(可能通过断言)确保我的 SampleAspect 在调用该联合点时进行拦截?

我无法断言来自 intercept() 的返回值,因为它除了执行关节点之外没有任何特殊作用。所以我的Junit找不到任何区别,无论是按方面执行还是基于返回值的常规执行。

如果提供有关方面测试的任何代码片段示例,那就太好了。谢谢

I am trying to write Junit tests for Custom Aspect. Here is the Aspect Class Snippet:

@Aspect
@Component
public class SampleAspect {

    private static Logger log = LoggerFactory.getLogger(SampleAspect.class);

    @Around("execution(* org.springframework.data.mongodb.core.MongoOperations.*(..)) || execution(* org.springframework.web.client.RestOperations.*(..))")
    public Object intercept(final ProceedingJoinPoint point) throws Throwable {
        logger.info("invoked Cutom aspect");
         return point.proceed();

    }

}

So the above aspect intercepts whenever jointpoint matches the pointcut. Its working fine.

But my question is how to unit test that class. I have the following Junit Test:

@Test(expected = MongoTimeoutException.class)
    public void TestWithMongoTemplate() {
        //MongoDocument class
        TestDocument test = new TestDocument();

        ApplicationContext ctx = new AnnotationConfigApplicationContext(TestMongoConfigurationMain.class);
        MongoTemplate mongoTemplate = ctx.getBean(MongoTemplate.class);

        //this call is being intercepted by SampleAspect
        mongoTemplate.save(test);

    }

So my mongoTemplate.save(test) in Junit is being intercepted by SampleAspect as it matches pointcut. But how should I make sure in junits(probably by asserting) that my SampleAspect is intercepting when that joint point is invoked?

I cannot assert on return value from intercept() as it does nothing special other than executing joint point. So my Junit cannot find any difference whether its being executed by aspect or a regular execution based on return values.

Any code snippets examples on aspect testing would be great if provided.Thanks

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

乱世争霸 2025-01-23 07:48:15

我认为您想要测试的是方面编织和切入点匹配。请注意,这将是集成而不是单元测试。如果您确实想对方面逻辑进行单元测试,并且因为您已经通过“mockito”标记了问题,我建议您这样做:编写一个单元测试并模拟方面的连接点以及可能的其他参数(如果有)。下面是一个稍微复杂一些的示例,其中包含一些方面内部逻辑:

要按方面定位的 Java 类:

package de.scrum_master.app;

public class Application {
    public static void main(String[] args) {
        new Application().doSomething(11);
        new Application().doSomething(-22);
        new Application().doSomething(333);
    }

    public void doSomething(int number) {
        System.out.println("Doing something with number " + number);
    }
}

测试方面:

package de.scrum_master.aspect;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;

@Aspect
public class SampleAspect {
    @Around("execution(* doSomething(int)) && args(number)")
    public Object intercept(final ProceedingJoinPoint thisJoinPoint, int number) throws Throwable {
        System.out.println(thisJoinPoint + " -> " + number);
        if (number < 0)
            return thisJoinPoint.proceed(new Object[] { -number });
        if (number > 99)
            throw new RuntimeException("oops");
        return thisJoinPoint.proceed();
    }
}

运行时的控制台日志 Application.main(..):

如您所见,方面传递 11,否定 -22 并抛出 333 的异常:

execution(void de.scrum_master.app.Application.doSomething(int)) -> 11
Doing something with number 11
execution(void de.scrum_master.app.Application.doSomething(int)) -> -22
Doing something with number 22
execution(void de.scrum_master.app.Application.doSomething(int)) -> 333
Exception in thread "main" java.lang.RuntimeException: oops
    at de.scrum_master.aspect.SampleAspect.intercept(SampleAspect.aj:15)
    at de.scrum_master.app.Application.doSomething(Application.java:10)
    at de.scrum_master.app.Application.main(Application.java:7)

方面的单元测试:

现在我们真的想验证这个方面确实它应该做什么并涵盖所有执行路径:

package de.scrum_master.aspect;

import org.aspectj.lang.ProceedingJoinPoint;
import org.junit.Rule;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;

import static org.mockito.Mockito.*;

public class SampleAspectTest {
    @Rule
    public MockitoRule mockitoRule = MockitoJUnit.rule();

    @Mock
    private ProceedingJoinPoint proceedingJoinPoint;

    private SampleAspect sampleAspect = new SampleAspect();

    @Test
    public void testPositiveSmallNumber() throws Throwable {
        sampleAspect.intercept(proceedingJoinPoint, 11);
        // 'proceed()' is called exactly once
        verify(proceedingJoinPoint, times(1)).proceed();
        // 'proceed(Object[])' is never called
        verify(proceedingJoinPoint, never()).proceed(null);
    }

    @Test
    public void testNegativeNumber() throws Throwable {
        sampleAspect.intercept(proceedingJoinPoint, -22);
        // 'proceed()' is never called
        verify(proceedingJoinPoint, never()).proceed();
        // 'proceed(Object[])' is called exactly once
        verify(proceedingJoinPoint, times(1)).proceed(new Object[] { 22 });
    }

    @Test(expected = RuntimeException.class)
    public void testPositiveLargeNumber() throws Throwable {
        sampleAspect.intercept(proceedingJoinPoint, 333);
    }
}

现在运行这个简单的 JUnit + Mockito 测试,以便单独测试方面逻辑,而不是布线/编织逻辑。对于后者,您需要另一种类型的测试。

PS:我只为你使用了 JUnit 和 Mockito。通常我只使用 Spock 及其内置的模拟功能。 <代码>;-)

I think what you are trying to test is aspect weaving and pointcut matching. Please note that that would be an integration rather than a unit test. If you really want to unit test your aspect logic and because you have tagged the question by "mockito" anyway, I suggest you do just that: Write a unit test and mock the aspect's joinpoint and maybe its other parameters, if any. Here is a slightly more complex example with some intra-aspect logic:

Java class to be targeted by aspect:

package de.scrum_master.app;

public class Application {
    public static void main(String[] args) {
        new Application().doSomething(11);
        new Application().doSomething(-22);
        new Application().doSomething(333);
    }

    public void doSomething(int number) {
        System.out.println("Doing something with number " + number);
    }
}

Aspect under test:

package de.scrum_master.aspect;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;

@Aspect
public class SampleAspect {
    @Around("execution(* doSomething(int)) && args(number)")
    public Object intercept(final ProceedingJoinPoint thisJoinPoint, int number) throws Throwable {
        System.out.println(thisJoinPoint + " -> " + number);
        if (number < 0)
            return thisJoinPoint.proceed(new Object[] { -number });
        if (number > 99)
            throw new RuntimeException("oops");
        return thisJoinPoint.proceed();
    }
}

Console log when running Application.main(..):

As you can see, the aspect passes on 11, negates -22 and throws an exception for 333:

execution(void de.scrum_master.app.Application.doSomething(int)) -> 11
Doing something with number 11
execution(void de.scrum_master.app.Application.doSomething(int)) -> -22
Doing something with number 22
execution(void de.scrum_master.app.Application.doSomething(int)) -> 333
Exception in thread "main" java.lang.RuntimeException: oops
    at de.scrum_master.aspect.SampleAspect.intercept(SampleAspect.aj:15)
    at de.scrum_master.app.Application.doSomething(Application.java:10)
    at de.scrum_master.app.Application.main(Application.java:7)

Unit test for aspect:

Now we really want to verify that the aspect does what it should and cover all execution paths:

package de.scrum_master.aspect;

import org.aspectj.lang.ProceedingJoinPoint;
import org.junit.Rule;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;

import static org.mockito.Mockito.*;

public class SampleAspectTest {
    @Rule
    public MockitoRule mockitoRule = MockitoJUnit.rule();

    @Mock
    private ProceedingJoinPoint proceedingJoinPoint;

    private SampleAspect sampleAspect = new SampleAspect();

    @Test
    public void testPositiveSmallNumber() throws Throwable {
        sampleAspect.intercept(proceedingJoinPoint, 11);
        // 'proceed()' is called exactly once
        verify(proceedingJoinPoint, times(1)).proceed();
        // 'proceed(Object[])' is never called
        verify(proceedingJoinPoint, never()).proceed(null);
    }

    @Test
    public void testNegativeNumber() throws Throwable {
        sampleAspect.intercept(proceedingJoinPoint, -22);
        // 'proceed()' is never called
        verify(proceedingJoinPoint, never()).proceed();
        // 'proceed(Object[])' is called exactly once
        verify(proceedingJoinPoint, times(1)).proceed(new Object[] { 22 });
    }

    @Test(expected = RuntimeException.class)
    public void testPositiveLargeNumber() throws Throwable {
        sampleAspect.intercept(proceedingJoinPoint, 333);
    }
}

Now run this simple JUnit + Mockito test in order to test the aspect logic in isolation, not the wiring/weaving logic. For the latter you would need another type of test.

P.S.: Only for you I used JUnit and Mockito. Usually I just use Spock and its built-in mocking capabilities. ;-)

场罚期间 2025-01-23 07:48:15

试试这个:

@RunWith(MockitoJUnitRunner.class)
public class SampleAspectTest {

    @InjectMocks
    private SampleAspect sampleAspect;

    @Mock
    private MongoOperations mongoOperations;

    private MongoOperations mongoOperationsWithAspect;

    @Before
    public void init() {
        AspectJProxyFactory factory = new AspectJProxyFactory(mongoOperations);
        factory.addAspect(sampleAspect);
        mongoOperationsWithAspect = factory.getProxy();
    }

    @Test
    public void testIntercept() {
        // stub
        when(mongoOperations.xxx(...)).thenReturn(...);
        // test
        mongoOperationsWithAspect.xxx();
        // verify
        verify(mongoOperationsWithAspect, times(1)).intercept(any());
    }
}

try this:

@RunWith(MockitoJUnitRunner.class)
public class SampleAspectTest {

    @InjectMocks
    private SampleAspect sampleAspect;

    @Mock
    private MongoOperations mongoOperations;

    private MongoOperations mongoOperationsWithAspect;

    @Before
    public void init() {
        AspectJProxyFactory factory = new AspectJProxyFactory(mongoOperations);
        factory.addAspect(sampleAspect);
        mongoOperationsWithAspect = factory.getProxy();
    }

    @Test
    public void testIntercept() {
        // stub
        when(mongoOperations.xxx(...)).thenReturn(...);
        // test
        mongoOperationsWithAspect.xxx();
        // verify
        verify(mongoOperationsWithAspect, times(1)).intercept(any());
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文