Отключение определенных аспектов во время модульных тестов - PullRequest
15 голосов
/ 15 сентября 2011

У меня есть интеграционные тесты (контекст загрузки) и модульные тесты, работающие вместе. Мой код выполняет ткачество времени компиляции с использованием Spring.

Моя проблема в том, что мои объявленные советы также выполняются во время некоторых из моих модульных тестов. Это убивает понятие модульного теста, поэтому я хотел бы отключить их.

Есть ли что-то, что я могу поместить в объявление pointcut, какой-то метод, который я могу вызвать, некоторую весеннюю конфигурацию или команду maven, которая отключает эти советы для чего-то вроде * UnitTest.java?

Спасибо за помощь.


пример:

У меня есть следующий модульный тест:

@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 равно нулю. Таким образом я вижу NullPointerException

Ответы [ 4 ]

7 голосов
/ 23 марта 2013

Ответ прост: вы хотите использовать if() выражение pointcut .


Обновление (после того, как вопрос также был обновлен): первоначально предоставленная ссылка должна содержать достаточно информации, но для чего она стоит, краткое объяснение и простой пример:

Pointcut if() - это метод аспекта static, возвращающий boolean. Если возвращаемое значение равно true, это означает, что любой комбинированный точечный вырез, такой как myPointcut() && if(), совпадает, пока совпадает myPointcut(). Для возвращаемого значения false весь комбинированный pointcut не совпадает, эффективно деактивируя любой совет, связанный с pointcut.

Так что вы можете сделать в статическом if() pointcut?

  • оценивает статический логический член некоторого класса инструментов, такого как TestMode.ACTIVE, что верно только во время модульного или интеграционного тестирования
  • оценить переменную среды, которая устанавливается только во время тестирования
  • оценивает системное свойство Java, которое устанавливается только во время тестирования
  • и многое другое

Если вы хотите сделать что-то более изощренное (и более хитрое), а производительность не так важна, вы также можете попытаться динамически определить, равна ли переменная-член аспекта с автоматическим подключением значение null или нет, и активировать ваши pointcuts, только если внедренный объект на самом деле присутствует. Единственная проблема здесь в том, как определить переменную-член из статического метода. Я понятия не имею о 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")
// ...
0 голосов
/ 20 ноября 2015

Вы можете написать метод, который возвращает, если текущее выполнение было запущено с использованием инфраструктуры JUnit.

Метод может проверять трассировку стека с помощью Thread.currentThread (). GetStackTrace () и искать наличие MockitoJUnitRunner.

Я протестировал это решение с помощью SpringJUnit4ClassRunner, но, думаю, мог бы работать с MockitoJUnitRunner.

Кроме того, вы можете получить статическое логическое поле, например:

private static boolean TEST_ENVIRONMENT = false;

В классе, представленном вваш проект (не в ваших тестах) и проверьте значение в методе управления вместо использования трассировки стека.

Когда вы запускаете свои тесты, вы можете использовать аннотацию @BeforeClass, чтобы установить TEST_ENVIRONMENT = true.

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

0 голосов
/ 15 сентября 2011

Переплетение времени компиляции включало бы вызовы советов в целевых методах, определенных вашими точечными вызовами. Лично я чувствую, что это хорошо для модульного тестирования с переплетением времени компиляции на месте, потому что во время выполнения ваш модуль включает класс с указанным советом?

Идея, которую я не должен включать в себя, заключается в том, чтобы иметь две разные цели компиляции, одну с ткачеством времени компиляции, а другую без, ты должен быть в состоянии сделать это с помощью профилей maven, профиля dev, не советов по ткачеству и профиль продукта для плетения аспектов.

0 голосов
/ 15 сентября 2011

Я могу только догадываться: Первое, что нужно иметь отдельный Spring applicationContext-test.xml, без компонентного сканирования; В maven вы можете добавить фазу выполнения, исключая ткацкие банки для теста.

...