单元测试异步延迟结果控制器永远挂起

Unit Test Async Deferred Result Controller gets hung forever

我正在测试的控制器方法

@GetMapping("/customers")
@ResponseBody
public DeferredResult<ResponseEntity<Resources<Resource<Customer>>>> getAllCustomers(
        @PageableDefault(page = 0, size = 20) @SortDefault.SortDefaults({
                @SortDefault(sort = "name", direction = Direction.ASC) }) Pageable pageable,
        PagedResourcesAssembler<Customer> assembler, HttpServletRequest request) {

    DeferredResult<ResponseEntity<Resources<Resource<Customer>>>> response = new DeferredResult<>(
            Long.valueOf(1000000));
    response.onTimeout(() -> response
            .setErrorResult(ResponseEntity.status(HttpStatus.REQUEST_TIMEOUT).body("Request timed out.")));
    response.onError((Throwable t) -> {
        response.setErrorResult(ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("An error occured."));
    });

    ListenableFuture<Page<Customer>> future = customerService.findAll(pageable);

    future.addCallback(new ListenableFutureCallback<Page<Customer>>() {

        @Override
        public void onSuccess(Page<Customer> result) {
            Link self = new Link(
                    ServletUriComponentsBuilder.fromRequestUri(request).buildAndExpand().toUri().toString(),
                    "self");
            LOGGER.debug("Generated Self Link {} for Customer Resource Collection", self.getHref());
            if (result.hasContent())
                response.setResult(
                        ResponseEntity.ok(assembler.toResource(result, customerResourceAssembler, self)));
            else
                response.setErrorResult(ResponseEntity.notFound());
            LOGGER.debug("Returning Response with {} customers", result.getNumber());
        }

        @Override
        public void onFailure(Throwable ex) {
            LOGGER.error("Could not retrieve customers due to error", ex);
            response.setErrorResult(ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                    .body("Could not save customers list due to server error."));
        }

    });

    return response;
}

单元测试

@RunWith(SpringRunner.class)
@WebMvcTest(CustomerController.class)
@EnableSpringDataWebSupport
@Import({ CustomerResourceAssember.class, BranchResourceAssembler.class, InvoiceResourceAssembler.class,
        CustomerAsyncService.class })
public class CustomerControllerTests {

    @Autowired
    private MockMvc mockMvc;

    @Autowired
    CustomerAsyncService customerService;

    @MockBean
    private CustomerRepository customerRepository;

    @Before
    public void setup() {
        MockitoAnnotations.initMocks(this);
    }

    @Test
    public void testWhenNoCustomersThenReturnsEmptyHALDocument() throws Exception {

        // Given
        BDDMockito.given(customerRepository.findAll(PageRequest.of(0, 20)))
                .willReturn(new PageImpl<Customer>(Collections.emptyList()));

        // When
        MvcResult result = mockMvc.perform(get("/customers").accept(MediaTypes.HAL_JSON_VALUE)).andDo(print())
                .andExpect(request().asyncStarted())
                .andExpect(request().asyncResult(new PageImpl<Customer>(Collections.emptyList()))).andReturn();

        // Then
        mockMvc.perform(asyncDispatch(result)).andExpect(status().isOk());

    }

这个测试从未完成,甚至没有在我的 IDE 上超时,我每次 运行 都必须杀死它,如果 运行 整个应用程序但是这个 /customers 当没有客户添加到应用程序时,端点会给出 404。

我需要做什么来确保这个测试完成,CustomerService 调用最终调用 CustomerRepository 我已经嘲笑了它,因为我无法思考如何模拟异步调用服务方式。客服class如下

@Async
@Service
public class CustomerAsyncService {

    private CustomerRepository customerRepository;

    @Autowired
    public CustomerAsyncService(CustomerRepository customerRepository) {
        this.customerRepository = customerRepository;
    }

    @Transactional(readOnly = true, isolation = Isolation.SERIALIZABLE)
    public ListenableFuture<Page<Customer>> findAll(Pageable pageable) {
        return AsyncResult.forValue(customerRepository.findAll(pageable));
    }

我希望模拟 Repository 方法可以解决问题。我如何模拟异步服务调用

我的错误是错误地使用了模拟,这有效

@RunWith(SpringRunner.class)
@WebMvcTest(CustomerController.class)
@Import({ CustomerResourceAssember.class, BranchResourceAssembler.class, InvoiceResourceAssembler.class,
        CustomerAsyncService.class })
public class CustomerControllerTests {

    @MockBean
    private CustomerRepository customerRepository;

    @InjectMocks
    CustomerAsyncService customerService = new CustomerAsyncService(customerRepository);

    @Before
    public void setup() {
        MockitoAnnotations.initMocks(this);
        JacksonTester.initFields(this, objectMapper);
    }

    @Test
    public void testReturnsNotFoundForEmptyGetAllCustomersResult() throws Exception {

        // Given
        Page<Customer> emptyPage = new PageImpl<Customer>(Collections.emptyList());
        BDDMockito.given(customerRepository.findAll(any(Pageable.class))).willReturn(emptyPage);

        // When
        MvcResult result = mockMvc.perform(get("/customers")).andExpect(request().asyncStarted()).andDo(print()).andReturn();

        // Then
        mockMvc.perform(asyncDispatch(result)).andDo(print()).andExpect(status().isNotFound());
    }


}