什么可能导致实现“ApplicationListener <contextrefreshedevent>”的类不被通知“ContextRefreshedEvent”</contextrefreshedevent>

时间:2014-07-13 17:37:50

标签: spring spring-test

我有一个 Spring应用程序监听器实现ApplicationListener<ContextRefreshedEvent>,如下所示:

@Profile({ Profiles.DEFAULT, Profiles.CLOUD, Profiles.TEST, Profiles.DEV })
@Component
public class BootstrapLoaderListener implements ApplicationListener<ContextRefreshedEvent>, ResourceLoaderAware, Ordered {

    private static final Logger log = Logger.getLogger(BootstrapLoaderListener.class);

    @Override
    public int getOrder() {
        return HIGHEST_PRECEDENCE;
    }

    @Autowired
    private DayToTimeSlotRepository dayToTimeSlotRepository;

    @Autowired
    private LanguageRepository languageRepository;

    private ResourceLoader resourceLoader;

    @Override
    @Transactional
    public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
        initApplication();
    }

    private void initApplication() {
        if (dayToTimeSlotRepository.count() == 0) {
            initDayToTimeSlots();
        }
        if (languageRepository.count() == 0) {
            initLanguages();
        }
    }

    private void initDayToTimeSlots() {
        for (Day day : Day.values()) {
            for (TimeSlot timeSlot : TimeSlot.values()) {
                DayToTimeSlot dayToTimeSlot = new DayToTimeSlot();
                dayToTimeSlot.setDay(day);
                dayToTimeSlot.setTimeSlot(timeSlot);
                dayToTimeSlot.setDisabled(isDayToTimeSlotDisabled(timeSlot, day));
                dayToTimeSlotRepository.save(dayToTimeSlot);
            }
        }
    }
 ...

我依赖这个监听器类来插入未更新或删除的引用数据,并且我有许多使用此类的Spring集成测试,其中一个失败,因为没有通知监听器(initDayToTimeSlots是没有被援引)。

我试图通过调试测试来确定问题的来源,我注意到当我自己运行有问题的测试类时,类中包含的测试传递(表示通知了侦听器)但是当我将所有应用程序测试类一起运行,不会通知侦听器导致测试失败(表明某些其他测试更改/污染了上下文)。

以下是有问题的测试类:

@ActiveProfiles({ Profiles.TEST })
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = { FullIntegrationTestConfiguration.class, BaseTestConfiguration.class })
public class RegularDayToTimeSlotsTest {

    private static int NUMBER_OF_REGULAR_DAY_TO_TIME_SLOTS_IN_WEEK = 25;

    @Before
    public void setup() {
        //org.hsqldb.util.DatabaseManagerSwing.main(new String[] { "--url", "jdbc:hsqldb:mem:bignibou", "--noexit" });
    }

    @Autowired
    private AdvertisementService advertisementService;

    @Test
    public void shouldNotContainSaturdayNorSunday() {
        Set<DayToTimeSlot> regularDayToTimeSlots = advertisementService.retrieveRegularDayToTimeSlots();
        assertThat(regularDayToTimeSlots).onProperty("day").excludes(Day.SATURDAY, Day.SUNDAY);
        assertThat(regularDayToTimeSlots).onProperty("day").contains(Day.MONDAY, Day.THUESDAY);
    }

    @Test
    public void shouldNotContainEveningNorNighttime() {
        Set<DayToTimeSlot> regularDayToTimeSlots = advertisementService.retrieveRegularDayToTimeSlots();
        assertThat(regularDayToTimeSlots).onProperty("timeSlot").excludes(TimeSlot.EVENING, TimeSlot.NIGHTTIME);
        assertThat(regularDayToTimeSlots).onProperty("timeSlot").contains(TimeSlot.MORNING, TimeSlot.LUNCHTIME);
    }

    @Test
    public void shouldContainCorrectNumberOfDayToTimeSlots() {
        Set<DayToTimeSlot> regularDayToTimeSlots = advertisementService.retrieveRegularDayToTimeSlots();
        assertThat(regularDayToTimeSlots).hasSize(NUMBER_OF_REGULAR_DAY_TO_TIME_SLOTS_IN_WEEK);
    }
}

我很困惑地看到prepareRefresh()方法中的finishRefresh()AbstractApplicationContext.refresh方法确实已被调用但我的听众未被通知...

有没有人有任何线索?

P.S。我知道我可以使用@DirtiesContext来获得新的上下文,我也知道最好不要依赖应用程序监听器进行测试,但我非常渴望了解这里出了什么问题。因此这篇文章。

编辑1 :当我单独调试有问题的测试类时,我注意到事件源的类型为GenericApplicationContext,并且如上所述,测试通过了OK,因为收到了监听器。但是,当所有测试类一起运行时,奇怪的是,事件源的类型为GenericWebApplicationContextSimpleApplicationEventMulticaster中没有找到侦听器:

@Override
    public void multicastEvent(final ApplicationEvent event) {
        for (final ApplicationListener<?> listener : getApplicationListeners(event)) {
            Executor executor = getTaskExecutor();
            if (executor != null) {
                executor.execute(new Runnable() {
                    @Override
                    public void run() {
                        invokeListener(listener, event);
                    }
                });
            }
            else {
                invokeListener(listener, event);
            }
        }
    }

编辑2 :我在编辑1 中的评论让我问自己有什么责任来确定上下文配置的唯一性......

例如,我只有两个具有以下上下文配置的测试类:

@ContextConfiguration(classes = { FullIntegrationTestConfiguration.class, BaseTestConfiguration.class })

我猜他们都会使用相同的缓存上下文,不是吗?现在第三个类可以使用相同的缓存上下文,即使它没有完全相同的上下文配置吗?

为什么我的测试得到GenericWebApplicationContext以上?

1 个答案:

答案 0 :(得分:1)

  

我在编辑1中的评论让我问自己有什么责任   确定上下文配置的唯一性......

构成上下文缓存键的元素在参考手册的“测试”一章的Context caching部分中有所描述。

  

例如,我只有两个具有以下上下文的测试类   配置:

     

@ContextConfiguration(classes = {   FullIntegrationTestConfiguration.class,BaseTestConfiguration.class})

     

我猜他们都会使用相同的缓存上下文,不是吗?

如果他们只按照确切的顺序声明那两个配置类,那么是。

  

现在,第三个类可以使用相同的缓存上下文,即使它没有   具有完全相同的上下文配置?

没有

  

为什么我的测试会获得上面的GenericWebApplicationContext?

只有在您的测试类(或其中一个超类)使用GenericWebApplicationContext进行注释时,才会加载@WebAppConfiguration

如果您遇到与此相反的行为,那么您发现了一个错误,如果您可以在issue repositorycreate a corresponding JIRA issue中针对“{3}}生成缩小的测试项目,我们将非常感谢Spring Framework“及其”测试“组件。

谢谢,

Sam(Spring TestContext Framework的作者)