Отключение определенных аспектов во время 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.

Это решение дает вам возможность узнать, работает ли ваш код из теста или нет.