【问题标题】:How to test Spring @EventListener method?如何测试 Spring @EventListener 方法?
【发布时间】:2021-04-08 04:06:30
【问题描述】:

我有一些活动发布:

@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

【问题讨论】:

  • 你想测试这个方法内部的功能,或者你想测试这个方法是否在这个事件上被调用?
  • 只是有没有被调用。
  • 你用的是spring-boot还是vanilla spring?
  • 只需注入一个模拟 ApplicationEventPublisher 并检查 publish 方法是否被调用。其他任何事情,您都在测试框架。
  • 我使用 SpringBoot 和 EasyMock。

标签: spring unit-testing


【解决方案1】:

首先,当您使用 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();
   }
}

【讨论】:

  • 这个verify方法来自哪里?
  • import static org.mockit.Mockito.verify;
  • 我使用 kotlin,即使我自动装配它,我也会收到发布者实例的未初始化错误。
【解决方案2】:

以防万一,启动整个 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 {}
}

【讨论】:

  • 如果您想自动装配一个ApplicationEventPublisher 并在仅加载应用程序上下文的一部分时寻找一个具体的实现,这个ApplicationContextRunner 也是要走的路!例如@SpringBootTest(classes=ApplicationContextRunner.class)
【解决方案3】:

我采取的选择是在测试本身中创建一个 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);
        }
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-12-13
    • 1970-01-01
    • 2019-06-14
    • 2018-10-22
    • 2021-12-26
    • 2015-04-24
    • 2018-09-05
    • 1970-01-01
    相关资源
    最近更新 更多