Многопоточный тест Spring Boot Mock Mvc не дает того же результата, что и во время выполнения - PullRequest
0 голосов
/ 23 апреля 2020

Я пытаюсь протестировать многопоточный код при загрузке Spring с Mock Mvc.

Код работает, как и ожидалось, во время выполнения, но выполняется в тесте начальной загрузки. Mock Mvc Результаты теста в пустом списке.

Исходный репозиторий содержит записи, даже в конструкторе реализации Callable , но когда вызывается call (), в его репозитории нет записей.

package com.xti.service.impl;

import com.xti.domain.Car;
import com.xti.repository.CarRepository;
import com.xti.service.CarService;
import com.xti.service.dto.CarDTO;
import com.xti.service.mapper.CarMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.LinkedList;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;

/**
 * Service Implementation for managing {@link Car}.
 */
@Service
@Transactional
public class CarServiceImpl implements CarService {

    private final Logger log = LoggerFactory.getLogger(CarServiceImpl.class);

    private final CarRepository carRepository;

    private final CarMapper carMapper;

    public CarServiceImpl(CarRepository carRepository, CarMapper carMapper) {
        this.carRepository = carRepository;
        this.carMapper = carMapper;
    }

    /**
     * Save a car.
     *
     * @param carDTO the entity to save.
     * @return the persisted entity.
     */
    @Override
    public CarDTO save(CarDTO carDTO) {
        log.debug("Request to save Car : {}", carDTO);
        Car car = carMapper.toEntity(carDTO);
        car = carRepository.save(car);
        return carMapper.toDto(car);
    }

    /**
     * Get all the cars.
     *
     * @return the list of entities.
     */
    @Override
    @Transactional(readOnly = true)
    public List<CarDTO> findAll() {
        log.debug("Request to get all Cars");
        ExecutorService executorService = Executors.newSingleThreadExecutor();
        CarTask carTask = new CarTask(carRepository);

        log.info("Original findAll().size() result {}", carRepository.findAll().size());
        try {
            return executorService.submit(carTask).get()
                .stream()
                .map(carMapper::toDto)
                .collect(Collectors.toCollection(LinkedList::new));

        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }

    // TODO why does this work at Runtime but not in MockMvc test
    public class CarTask implements Callable<List<Car>> {

        private final CarRepository otherCarRepository;

        public CarTask(CarRepository otherCarRepository) {
            this.otherCarRepository = otherCarRepository;
            log.info("From CarTask constructor call() findAll().size()  {}", otherCarRepository.findAll().size());
        }


        @Override
        public List<Car> call() {
            log.info("From CarTask call() findAll().size()  {}", otherCarRepository.findAll().size());
            return otherCarRepository.findAll();
        }
    }

    /**
     * Get one car by id.
     *
     * @param id the id of the entity.
     * @return the entity.
     */
    @Override
    @Transactional(readOnly = true)
    public Optional<CarDTO> findOne(Long id) {
        log.debug("Request to get Car : {}", id);
        return carRepository.findById(id)
            .map(carMapper::toDto);
    }

    /**
     * Delete the car by id.
     *
     * @param id the id of the entity.
     */
    @Override
    public void delete(Long id) {
        log.debug("Request to delete Car : {}", id);
        carRepository.deleteById(id);
    }
}

CarResourceIT

package com.xti.web.rest;

import com.xti.MultithreadedTestingApp;
import com.xti.domain.Car;
import com.xti.repository.CarRepository;
import com.xti.service.CarService;
import com.xti.service.dto.CarDTO;
import com.xti.service.mapper.CarMapper;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.transaction.annotation.Transactional;
import javax.persistence.EntityManager;
import java.util.List;

import static org.assertj.core.api.Assertions.assertThat;
import static org.hamcrest.Matchers.hasItem;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
package com.xti.web.rest;
@SpringBootTest(classes = MultithreadedTestingApp.class)
@AutoConfigureMockMvc
@WithMockUser
public class CarResourceIT {
private static final String DEFAULT_MODEL = "AAAAAAAAAA";
private static final String UPDATED_MODEL = "BBBBBBBBBB";

@Autowired
private CarRepository carRepository;

@Autowired
private CarMapper carMapper;

@Autowired
private CarService carService;

@Autowired
private EntityManager em;

@Autowired
private MockMvc restCarMockMvc;

private Car car;


/**
 * Create an entity for this test.
 *
 * This is a static method, as tests for other entities might also need it,
 * if they test an entity which requires the current entity.
 */
public static Car createEntity(EntityManager em) {
    Car car = new Car()
        .model(DEFAULT_MODEL);
    return car;
}
/**
 * Create an updated entity for this test.
 *
 * This is a static method, as tests for other entities might also need it,
 * if they test an entity which requires the current entity.
 */
public static Car createUpdatedEntity(EntityManager em) {
    Car car = new Car()
        .model(UPDATED_MODEL);
    return car;
}

@BeforeEach
public void initTest() {
    car = createEntity(em);
}

@Test
@Transactional
public void createCar() throws Exception {
    int databaseSizeBeforeCreate = carRepository.findAll().size();

    // Create the Car
    CarDTO carDTO = carMapper.toDto(car);
    restCarMockMvc.perform(post("/api/cars")
        .contentType(MediaType.APPLICATION_JSON)
        .content(TestUtil.convertObjectToJsonBytes(carDTO)))
        .andExpect(status().isCreated());

    // Validate the Car in the database
    List<Car> carList = carRepository.findAll();
    assertThat(carList).hasSize(databaseSizeBeforeCreate + 1);
    Car testCar = carList.get(carList.size() - 1);
    assertThat(testCar.getModel()).isEqualTo(DEFAULT_MODEL);
}

@Test
@Transactional
public void createCarWithExistingId() throws Exception {
    int databaseSizeBeforeCreate = carRepository.findAll().size();

    // Create the Car with an existing ID
    car.setId(1L);
    CarDTO carDTO = carMapper.toDto(car);

    // An entity with an existing ID cannot be created, so this API call must fail
    restCarMockMvc.perform(post("/api/cars")
        .contentType(MediaType.APPLICATION_JSON)
        .content(TestUtil.convertObjectToJsonBytes(carDTO)))
        .andExpect(status().isBadRequest());

    // Validate the Car in the database
    List<Car> carList = carRepository.findAll();
    assertThat(carList).hasSize(databaseSizeBeforeCreate);
}


@Test
@Transactional
public void getAllCars() throws Exception {
    // TODO why does CarRepository not contain any Cars?
    // Initialize the database
    carRepository.saveAndFlush(car);

    // Get all the carList
    restCarMockMvc.perform(get("/api/cars?sort=id,desc"))
        .andExpect(status().isOk())
        .andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE))
        .andExpect(jsonPath("$.[*].id").value(hasItem(car.getId().intValue())))
        .andExpect(jsonPath("$.[*].model").value(hasItem(DEFAULT_MODEL)));
}

@Test
@Transactional
public void getCar() throws Exception {
    // Initialize the database
    carRepository.saveAndFlush(car);

    // Get the car
    restCarMockMvc.perform(get("/api/cars/{id}", car.getId()))
        .andExpect(status().isOk())
        .andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE))
        .andExpect(jsonPath("$.id").value(car.getId().intValue()))
        .andExpect(jsonPath("$.model").value(DEFAULT_MODEL));
}

@Test
@Transactional
public void getNonExistingCar() throws Exception {
    // Get the car
    restCarMockMvc.perform(get("/api/cars/{id}", Long.MAX_VALUE))
        .andExpect(status().isNotFound());
}

@Test
@Transactional
public void updateCar() throws Exception {
    // Initialize the database
    carRepository.saveAndFlush(car);

    int databaseSizeBeforeUpdate = carRepository.findAll().size();

    // Update the car
    Car updatedCar = carRepository.findById(car.getId()).get();
    // Disconnect from session so that the updates on updatedCar are not directly saved in db
    em.detach(updatedCar);
    updatedCar
        .model(UPDATED_MODEL);
    CarDTO carDTO = carMapper.toDto(updatedCar);

    restCarMockMvc.perform(put("/api/cars")
        .contentType(MediaType.APPLICATION_JSON)
        .content(TestUtil.convertObjectToJsonBytes(carDTO)))
        .andExpect(status().isOk());

    // Validate the Car in the database
    List<Car> carList = carRepository.findAll();
    assertThat(carList).hasSize(databaseSizeBeforeUpdate);
    Car testCar = carList.get(carList.size() - 1);
    assertThat(testCar.getModel()).isEqualTo(UPDATED_MODEL);
}

@Test
@Transactional
public void updateNonExistingCar() throws Exception {
    int databaseSizeBeforeUpdate = carRepository.findAll().size();

    // Create the Car
    CarDTO carDTO = carMapper.toDto(car);

    // If the entity doesn't have an ID, it will throw BadRequestAlertException
    restCarMockMvc.perform(put("/api/cars")
        .contentType(MediaType.APPLICATION_JSON)
        .content(TestUtil.convertObjectToJsonBytes(carDTO)))
        .andExpect(status().isBadRequest());

    // Validate the Car in the database
    List<Car> carList = carRepository.findAll();
    assertThat(carList).hasSize(databaseSizeBeforeUpdate);
}

@Test
@Transactional
public void deleteCar() throws Exception {
    // Initialize the database
    carRepository.saveAndFlush(car);

    int databaseSizeBeforeDelete = carRepository.findAll().size();

    // Delete the car
    restCarMockMvc.perform(delete("/api/cars/{id}", car.getId())
        .accept(MediaType.APPLICATION_JSON))
        .andExpect(status().isNoContent());

    // Validate the database contains one less item
    List<Car> carList = carRepository.findAll();
    assertThat(carList).hasSize(databaseSizeBeforeDelete - 1);
}

}

В тесте используется H2 с этой конфигурацией:

spring:
  application:
    name: multithreaded_testing
  datasource:
    type: com.zaxxer.hikari.HikariDataSource
    url: jdbc:h2:mem:multithreaded_testing;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
    name:
    username:
    password:
    hikari:
      auto-commit: false

Неудачный тест: com.xti. web.rest.CarResourceIT # getAllCars

Impl: com.xti.service.impl.CarServiceImpl # findAll

https://bitbucket.org/Kelvijn/mockmvc_multithreaded_problem/src/master/

...