【问题标题】:CDI: Interceptor is not invoked when called from a JUnit testCDI:从 JUnit 测试调用时不调用拦截器
【发布时间】:2014-08-06 17:18:07
【问题描述】:

我在JBoss documentation之后创建了一个拦截器。

为了测试拦截器,我输入:

@Interceptor
@Transactional
public class TransactionalInterceptor {
  @AroundInvoke
  public Object intercept(InvocationContext ctx) throws Exception {
    System.out.println("intercept!");
    return ctx.proceed();
  }
}

现在我想在单元测试中测试这个拦截器,使用the WeldJUnit4Runner class

@RunWith(WeldJUnit4Runner.class)
public class MyTest {
  @Test
  @Transactional  // the interceptor I created
  public void testMethod() {
    System.out.println("testMethod");
    anotherMethod();
  }

  @Transactional
  public void anotherMethod() {
    System.out.println("anotherMethod");
  }
}

现在预期的输出当然是

intercept!
testMethod
intercept!
anotherMethod

但是,输出却是

intercept!
testMethod
anotherMethod

主要问题是,如果我将一个 bean 注入到我的测试中,这也是如此:我调用的 bean 的第一个方法被拦截,但如果此方法调用另一个方法,则不会调用拦截器。

非常感谢任何想法!


我只是尝试按照@adrobisch 的建议修改我的代码,这很有效:

@RunWith(WeldJUnit4Runner.class)
public class MyTest {
  @Inject
  private MyTest instance;

  @Test
  @Transactional  // the interceptor I created
  public void testMethod() {
    System.out.println("testMethod");
    instance.anotherMethod();
  }

  @Transactional
  public void anotherMethod() {
    System.out.println("anotherMethod");
  }
}

输出是(如预期的那样)

intercept!
testMethod
intercept!
anotherMethod

但是,以下操作不起作用

@RunWith(WeldJUnit4Runner.class)
public class MyTest {
  @Inject
  private MyTest instance;

  @Test
  // @Transactional  <- no interceptor here!
  public void testMethod() {
    System.out.println("testMethod");
    instance.anotherMethod();
  }

  @Transactional
  public void anotherMethod() {
    System.out.println("anotherMethod");
  }
}

这里的输出是

testMethod
anotherMethod

不过,这似乎符合规范!现在一切都很好。

【问题讨论】:

  • 我对您编辑中的行为进行了一些研究,我认为从 CDI 的角度来看,从 testMethod 调用 anotherMethod 被视为业务方法调用,因此不应用拦截器。请参阅 github.com/cdi-spec/cdi-spec.org/blob/master/_faq/core/… 以获取更多参考信息和 CDI 规范的链接。

标签: java jboss weld weld-se


【解决方案1】:

拦截器是使用代理实现的。由于第二个方法是从对象实例中调用的,所以调用不能被代理捕获,因此不能被拦截。为此,您需要引用 bean 的 CDI 代理。

【讨论】:

  • 非常感谢!使用此信息,我还发现了 this question 关于同一问题。您是否碰巧知道某种工具可以告知源代码中使用这些错误方法调用的位置?
  • 我不会称这些调用不正确,它们只是您 bean 内部的业务方法调用。工具很难检测到这些调用(考虑缺少拦截器注释的接口等),因此您只需考虑要在哪里应用拦截器并相应地创建客户端 bean。
【解决方案2】:

可以使用 DeltaSpike 在正确初始化的 CDI bean 上运行测试,尽管它的文档和错误消息在不完全正确时不是很有帮助。这是让@Transactional 拦截器工作的方法:

@Transactional // the @Transactional from org.apache.deltaspike.jpa.api.transaction
@TestControl(startScopes = { TransactionScoped.class })
@RunWith(CdiTestRunner.class)
public MyTestClass { ... }

然后添加:

deltaspike.testcontrol.use_test_class_as_cdi_bean=true

到 src/test/resources/META-INF/apache-deltaspike.properties

不幸的是,@Transactional(readOnly = true) 不起作用 - 不知道为什么。而且,与 Spring 等价物不同的是,它不会回滚事务,也不会在同一事务中运行 @Before/@After。

但是对于另一个拦截器,您只需要围绕测试方法本身就可以了。

【讨论】:

  • TestControl 只控制标准。 CDI-上下文。如果您认为需要通过 TestControl 处理 TransactionScoped,@Transactional(readOnly = true) 将无法工作,因为在这种情况下,处理两者的拦截器只是不活动的。如果您使用 Weld,请确保在测试模块中也启用事务拦截器...
猜你喜欢
  • 2012-08-03
  • 2018-09-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-01-28
  • 1970-01-01
  • 2015-04-16
  • 2020-06-17
相关资源
最近更新 更多