CDI: перехватчик не вызывается при вызове из теста JUnit

Я создал перехватчик в соответствии с документацией JBoss.

Для проверки перехватчика поставил:

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

Теперь я хотел протестировать этот перехватчик в модульном тесте, используя класс WeldJUnit4Runner.

@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

Однако это, похоже, соответствует спецификации! Теперь все в порядке.


person user3151902    schedule 16.06.2014    source источник
comment
Я провел некоторое исследование поведения в вашем редактировании и думаю, что с точки зрения CDI вызов anotherMethod из testMethod рассматривается как вызов бизнес-метода, поэтому перехватчики не применяются. См. github.com/cdi-spec/cdi-spec.org/blob/master/_faq/core/ для дальнейшего ознакомления и ссылки на спецификацию CDI.   -  person adrobisch    schedule 18.06.2014


Ответы (2)


Перехватчики реализованы с использованием прокси. Поскольку второй метод вызывается из экземпляра объекта, вызов не может быть перехвачен прокси-сервером и, следовательно, не может быть перехвачен. Для этого вам понадобится ссылка на прокси-сервер CDI вашего компонента.

person adrobisch    schedule 16.06.2014
comment
Большое спасибо! Используя эту информацию, я также нашел этот вопрос по той же проблеме. Вы случайно не знаете какой-нибудь инструмент, который может информировать о местах в исходном коде, где используются эти некорректные вызовы методов? - person user3151902; 17.06.2014
comment
Я бы не назвал эти вызовы неправильными, это просто вызовы бизнес-методов внутри ваших бинов. Для инструмента было бы очень сложно обнаружить эти вызовы (думая об интерфейсах, в которых отсутствуют аннотации перехватчиков и т. д.), поэтому вам просто нужно подумать о том, где вы хотите применить перехватчики, и соответственно создать клиентские компоненты. - person adrobisch; 18.06.2014

С помощью DeltaSpike можно запускать тесты правильно инициализированного компонента CDI, хотя его документация и сообщения об ошибках не очень полезны, когда это не совсем правильно. Вот как это сделать, чтобы заставить работать перехватчик @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 в той же транзакции.

Но для другого перехватчика вам нужен только сам метод тестирования, все должно быть в порядке.

person Alex Hayward    schedule 05.06.2015
comment
TestControl просто контролирует std. CDI-контексты. @Transactional(readOnly = true) не может работать, если вы считаете, что вам нужно обрабатывать TransactionScoped через TestControl, потому что в этом случае перехватчик, который обрабатывает оба, просто не активен. Если вы используете Weld, убедитесь, что вы также включили перехватчик транзакций в тестовом модуле... - person Dar Whi; 13.07.2015