Отключение определенных аспектов во время unit test запусков
У меня есть интеграционные тесты (контекст нагрузки) и единичные тесты, выполняющиеся вместе. Мой код делает aspectj компилировать временное тканье с помощью spring.
Моя проблема в том, что мои объявленные рекомендации также выполняются во время некоторых моих модульных тестов. Это убивает понятие unit test, поэтому я хотел бы отключить их.
Есть ли что-то, что я могу наложить на объявление pointcut, некоторый метод, который я могу вызвать, некоторая конфигурация spring или команда maven, которая отключает эти рекомендации для чего-то вроде всего * UnitTest.java?
Спасибо за помощь.
Пример:
У меня есть следующий unit test:
@RunWith(MockitoJUnitRunner.class)
public class CompanyServiceImplTest {
@Test
public void createCampaignTest() throws Exception {
when(companyDaoMock.saveCompany(any(Campaign.class))).thenReturn(77L);
Long campaignId = companyService.createCampaign(campaignMock);
assertEquals(Long.valueOf(77L), Long.valueOf(campaignId));
}
}
и следующий метод обслуживания:
@Override
@Transactional
@EventJournal(type = EventType.CAMPAIGN_CREATE, owner = EventOwner.TERMINAL_USER)
public Long createCampaign(Campaign campaign) {
return companyDao.saveCompany(campaign);
}
аспект:
@Aspect
public class EventJournalAspect {
@Autowired
private EventJournalService eventJournalService;
@Pointcut(value="execution(public * *(..))")
public void anyPublicMethod() {}
@Pointcut("within(com.terminal.service..*)")
private void inService() {}
@AfterReturning(pointcut = "anyPublicMethod() && inService() && @annotation(eventJournal) && args(entity,..)", returning = "id")
public void process(Object id, EventJournal eventJournal, AbstractDomainEntity entity)
throws Throwable {
if (eventJournal.type() != EventType.CAMPAIGN_PAYMENT || id != null) {
saveEvent(eventJournal, EventStatus.SUCCESS, entity, (Long) id);
}
}
@AfterThrowing(pointcut = "anyPublicMethod() && inService() && @annotation(eventJournal) && args(entity,..)", throwing="ex")
public void processException(EventJournal eventJournal, AbstractDomainEntity entity, Exception ex) throws Throwable {
saveEvent(eventJournal, EventStatus.FAILURE, entity, null);
}
private void saveEvent(EventJournal eventJournal, EventStatus status, AbstractDomainEntity entity, Long persistentId) {
EventType type = eventJournal.type();
EventOwner owner = eventJournal.owner();
eventJournalService.saveEvent(type, owner, EventStatus.SUCCESS, entity, persistentId);
}
}
Когда тест выполняется - eventJournalService
имеет значение NULL. Таким образом, я вижу NullPointerException
Ответы
Ответ 1
Ответ прост: вы хотите использовать выражение if()
pointcut.
Обновить (после того, как вопрос также был обновлен): Первоначально предоставленная ссылка выше должна содержать достаточно информации, но для чего она стоит, краткое объяснение и простой пример:
An if()
pointcut - это метод аспект static
, возвращающий boolean
. Если возвращаемое значение true
, это означает, что любой комбинированный pointcut, например myPointcut() && if()
, совпадает с myPointcut()
. Для возвращаемого значения false
весь комбинированный pointcut не соответствует, эффективно деактивируя любые советы, связанные с pointcut.
Итак, что вы можете сделать в статическом pointcut if()
?
- оценить статический логический элемент некоторого инструментального класса, например
TestMode.ACTIVE
, который справедлив только во время тестирования единицы или интеграции.
- оценить переменную среды, которая устанавливается только во время тестирования.
- оценить системное свойство Java, которое устанавливается только во время тестирования
- и многое другое
Если вы хотите сделать что-то более странное (и сложнее), а производительность не так важна, вы также можете попытаться динамически определить, равна ли переменная элемента с переменным проводником равным нулю или нет, и только активировать ваши точки, если введенный объект фактически данный. Единственная проблема здесь заключается в том, как определить переменную-член из статического метода. Я понятия не имею о Spring AOP, но в простом AspectJ есть вспомогательный класс Aspects
с несколькими перегруженными методами с именем aspectOf(..)
. Предполагая, что ваш аспект создан как одиночный, вы можете сделать что-то вроде этого:
@Pointcut("if()")
public static boolean isActive() {
return Aspects.aspectOf(PerformanceMonitorAspect.class).eventJournalService != null;
}
// ...
@AfterReturning(pointcut = "isActive() && anyPublicMethod() && inService() && @annotation(eventJournal) && args(entity,..)", returning = "id")
// ...
@AfterThrowing(pointcut = "isActive() && anyPublicMethod() && inService() && @annotation(eventJournal) && args(entity,..)", throwing="ex")
// ...
Ответ 2
Я могу только догадываться:
Во-первых, нужно иметь отдельный test2, applicationContext-test.xml Spring
без компонентного сканирования;
В maven вы можете добавить фазу, исключая плетение банок для тестирования.
Ответ 3
Время компиляции времени будет включать в себя призывы к вызовам в целевых методах, идентифицированных указанными вами точками. Я лично чувствую, что хорошо unit test со временем скомпилировать время компиляции, потому что во время выполнения ваш блок включает класс с рекомендацией в тексте?
Мысль о том, что я не должен включать в себя советы, состоит в том, чтобы иметь две разные цели компиляции: одну с компиляцией во времени и одну без нее, вы должны иметь возможность делать это через профили maven, профиль dev, не поддерживающий советы и prod, чтобы сплести аспекты.
Ответ 4
Вы можете написать метод, который возвращает, если текущее исполнение было запущено с использованием инфраструктуры JUnit.
Метод может проверять трассировку стека с помощью Thread.currentThread(). getStackTrace() и искать присутствие MockitoJUnitRunner.
Я тестировал это решение с помощью SpringJUnit4ClassRunner, но я думаю, что он мог бы работать с MockitoJUnitRunner.
Кроме того, вы можете получить статическое логическое поле типа:
private static boolean TEST_ENVIRONMENT = false;
В классе, присутствующем в вашем проекте (не в ваших тестах), и проверьте значение в методе управления вместо использования трассировки стека.
Когда вы запускаете свои тесты, вы можете использовать аннотацию @BeforeClass, чтобы установить TEST_ENVIRONMENT = true.
Это решение дает вам возможность узнать, работает ли ваш код из теста или нет.