如何测试Spring @EventListener方法?

时间:2018-06-20 14:29:47

标签: spring unit-testing

我有一些活动发布:

@Autowired private final ApplicationEventPublisher publisher;
...
publisher.publishEvent(new MyApplicationEvent(mySource));

我有此事件侦听器:

class MyApplicationEventHandler {

    @Autowired SomeDependency someDependency;

    @EventListener public void processEvent(final MyApplicationEvent event) {
        // handle event...
    }
}

我需要使用EasyMock对其进行测试。有没有一种简单的方法可以在测试中发布某些内容并断言我的事件侦听器做了什么?

编辑:

我试图创建这样的模拟测试:

// testing class
SomeDependency someDependency = mock(SomeDependency.class);

MyApplicationEventHandler tested = new MyApplicationEventHandler(someDependency);

@Autowired private final ApplicationEventPublisher publisher;

@Test
public void test() {
    someDependency.doSomething(anyObject(SomeClass.class));
    replay();
    publisher.publishEvent(new MyApplicationEvent(createMySource()));
}

没有用。

java.lang.AssertionError: 
  Expectation failure on verify:
    SomeDependency.doSomething(<any>): expected: 1, actual: 0

3 个答案:

答案 0 :(得分:6)

以防万一,不能选择扩展整个Spring上下文,因此引入了Spring Boot 2.0.0 ApplicationContextRunner

ApplicationContextRunner可以在测试中创建应用程序上下文,从而可以对上下文进行更多控制。

完整的测试示例可以是:

package net.andreaskluth.context.sample;

import static org.assertj.core.api.Assertions.assertThat;

import java.util.concurrent.atomic.AtomicBoolean;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;

public class SimpleEventTest {

  private final ApplicationContextRunner runner = new ApplicationContextRunner();

  @Test
  public void happyPathSuccess() {
    AtomicBoolean sideEffectCausedByEvent = new AtomicBoolean(false);
    ObservableEffect effect = () -> sideEffectCausedByEvent.set(true);

    runner
        .withBean(SomeEventListener.class, effect)
        .run(
            context -> {
              context.publishEvent(new SomeEvent());
              assertThat(sideEffectCausedByEvent.get()).isTrue();
            });
  }

  public interface ObservableEffect {
    void effect();
  }

  @Component
  public static class SomeEventListener {

    private final ObservableEffect effect;

    public SomeEventListener(ObservableEffect effect) {
      this.effect = effect;
    }

    @EventListener(SomeEvent.class)
    public void listen() {
      effect.effect();
    }
  }

  public static class SomeEvent {}
}

答案 1 :(得分:2)

首先,当您使用Spring Boot时,对它们的测试变得非常简单。此测试将启动引导上下文并注入ApplicationEventPublisher的真实实例,但创建模拟的SomeDependency实例。该测试将发布所需的事件,并验证您的模拟是否按预期被调用。

@RunWith(SpringRunner.class)
@SpringBootTest
public class EventPublisherTest {

   @Autowired 
   private final ApplicationEventPublisher publisher;

   @MockBean
   private SomeDependency someDependency;

   @Test
   public void test() {
      publisher.publishEvent(new MyApplicationEvent(createMySource()));

      // verify that your method in you 
      verify(someDependency, times(1)).someMethod();
   }
}

答案 2 :(得分:0)

我采取的选择是在测试本身中创建一个TestConfiguration和一个侦听器,如下所示:

@Slf4j
@ExtendWith(SpringExtension.class)
@SpringBootTest(classes = {RegistrationConfiguration.class, RegistrationTestConfiguration.class})
class RegistrationServiceIntegrationTest {

    @Autowired
    private DynamoDBMapper dynamoDBMapper;
    @Autowired
    private AmazonDynamoDBAsync amazonDynamoDB;
    @Autowired
    private RegistrationRepository repository;
    @Autowired
    private ApplicationEventPublisher eventPublisher;

    private String personaId;

    private RegistrationService testObj = null;

    @BeforeEach void setup() {
        RegistrationServiceDBHelper.initializeTable(dynamoDBMapper, amazonDynamoDB, repository);
        personaId = UUID.randomUUID().toString();
        testObj = new RegistrationService(repository, eventPublisher);
    }


    @TestConfiguration
    static class RegistrationTestConfiguration {
        @Bean
        RegistrationServiceEventListener eventListener() {
            return new RegistrationServiceEventListener();
        }
    }


    @Component
    public static class RegistrationServiceEventListener {
        @TransactionalEventListener
        public void onPlayerRegisteredEvent(PlayerRegisteredEvent event) {
            log.info("Received new user event {}", event.personaId());
            assertThat(event.personaId()).isNotBlank();
        }
    }


    @DisplayName("given a persona id that is not registered")
    @Nested class WhenNoPlayerIsRegistered {

        @DisplayName("when a player is registered"
                + " then an event is raised")
        @Test void raisePlayerRegisteredEvent() {
            // When
            testObj.registerPlayer(personaId);
        }
    }
}