【问题标题】:Spring - How to test Controller with ApplicationEventPublisher dependency?Spring - 如何使用 ApplicationEventPublisher 依赖项测试控制器?
【发布时间】:2018-08-30 12:20:14
【问题描述】:

我有一个控制器正在发布一个事件

@RestController
public class Controller
{
    @Autowired
    private ApplicationEventPublisher publisher;

    @GetMapping("/event")
    public void get()
    {
        publisher.publishEvent(new Event());
    }
}

现在我想测试该事件是否已发布。首先,我尝试@MockBean ApplicationEventPublisher 并验证方法调用。但是根据https://jira.spring.io/browse/SPR-14335这是行不通的

所以我是这样做的:

@RunWith(SpringRunner.class)
@WebMvcTest(controllers = Controller.class)
public class ControllerTest
{
    @Autowired
    private MockMvc mockMvc;

    @Test
    public void getTest() throws Exception
    {
        this.mockMvc.perform(get("/").contentType(MediaType.APPLICATION_JSON)
                    .andExpect(status().isOk());
        assertNotNull(Listener.event);
    }

    @TestConfiguration
    static class Listener
    {
        public static Event event;

        @EventListener
        void listen ( Event incoming )
        {
            event = incoming;
        }
    }
}

对于这个常见的用例有没有更简单的方法?

【问题讨论】:

    标签: spring spring-boot testing event-handling


    【解决方案1】:

    你可以这样做

    @RunWith(SpringRunner.class)
    public class ControllerTest {
    
        private MockMvc mockMvc;
    
        @MockBean
        private ApplicationEventPublisher publisher;
    
        @Before
        public void setup() {
            Controller someController= new Controller(publisher);
            mockMvc = MockMvcBuilders.standaloneSetup(someController).build();
        }
    
        @Test
        public void getTest() throws Exception
        {
            ArgumentCaptor<Event> argumentCaptor = ArgumentCaptor.forClass(Event.class);
            doAnswer(invocation -> {
                Event value = argumentCaptor.getValue();
                 //assert if event is correct
                return null;
            }).when(publisher).publishEvent(argumentCaptor.capture());  
    
            this.mockMvc.perform(get("/").contentType(MediaType.APPLICATION_JSON))
                    .andExpect(status().isOk());
    
            verify(publisher, times(1)).publishEvent(any(Event.class));
        }
    
    }  
    

    并且还要在你的控制器类中将字段注入更改为构造函数注入(这是一个很好的做法)。

    @RestController
    public class Controller
    {
    
      private ApplicationEventPublisher publisher;
    
      @Autowired
      public Controller(ApplicationEventPublisher publisher) {
          this.publisher = publisher;
      }
     ....
    }
    

    【讨论】:

    • 这是一个非常好的方法。我不知道:mockMvc = MockMvcBuilders.standaloneSetup(someController).build(); 只是这部分有点烦人:classes = MockServletContext.class@SpringBootTest。是否没有内置注释来启用此行为?
    • @davidxxx 实际上根本不需要。即使没有它也可以工作。编辑了我的答案
    • 这样还是比较好。
    • 好的,诀窍是手动将 Mocked 发布者注入控制器。所以你不需要 MockBean 而只需要 Mock 注释。
    • 你,@Mock 就够了。由于您已经提到的错误,我们必须这样做
    【解决方案2】:

    另一种可能性是您将控制器上的 ApplicationEventPublisher 实例替换为在测试中使用反射的模拟实例:

    public class ControllerTest {
    ...
        // Collect your controller
        @Autowired
        private Controller controller;
    
        // Use the mock publisher
        @MockBean
        private ApplicationEventPublisher publisherMock;
    
        // E.g. in setup set the mock publisher on your controller
        @Before
        public void setup() {
            ReflectionTestUtils.setField(controller, "publisher", publisherMock);
        }
    ...
    

    【讨论】:

      【解决方案3】:

      我面临同样的问题,现在我使用 TestConfiguration 解决了这个问题:

      @SpringBootTest
      class MyUseCaseIT {
      
        @Autowired private ApplicationEventPublisher publisher;
        @Autowired private MyService service;
      
      @Test
        void callUseCase() {
      var event = mock(MyEvent.class);
          doNothing().when(publisher).publishEvent(event);
      
      service.useCase();
      
          verify(publisher).publishEvent(event);
      }
      
      
      @TestConfiguration
        static class MockitoPublisherConfiguration {
      
          @Bean
          @Primary
          ApplicationEventPublisher publisher() {
            return mock(ApplicationEventPublisher.class);
          }
        }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-06-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-10-27
        • 1970-01-01
        相关资源
        最近更新 更多