Лучшие практики тестирования Spring Controller - PullRequest
0 голосов
/ 14 февраля 2019

В моем приложении у меня есть контроллер, основанный на заданном шаблоне:

public class Controller {

@Autowired
Mapper mapper;

@Autowired
Service service;

public EntityDto create(EntityDto dto) {
    Entity entity = mapper.mapToEntity(dto);
    Entity saved = service.save(entity);
    return mapper.mapToDto(saved);
}

Каков хороший подход к таким классам тестирования?Я вижу несколько возможностей:

  1. макетировать все с помощью Mockito и проверять, передается ли объект, полученный из одного макета, в другой
  2. делать интеграционные тесты с контекстом Spring, работающим
  3. пропустить тест контроллера, так как он не содержит бизнес-логики

Все ли в порядке?Может быть, другой путь?

Ответы [ 7 ]

0 голосов
/ 28 февраля 2019

1 & 2

Я думаю, что вы должны протестировать все классы с библиотекой макетов во всех слоях.Даже самые простые классы хороши для тестирования.

Тогда вам нужно пройти интеграционные тесты.

Я думаю, что интеграционные тесты также можно запускать постоянно, если они не требуют работы внешних систем.Единственное, что может быть временем.Я не был в проектах, где несколько минут ожидания для интеграционных тестов было бы слишком много.

Базы данных в памяти и интегрированные тестовые библиотеки / инфраструктуры (например, @SpringBootTest) хороши для интеграционных тестов.

Я думаю, что хорошие тесты позволяют проводить много рефакторинга.Интеграционные тесты не сильно ломаются, если вы выполняете рефакторинг.Тесты JUnit могут сломаться, но я не думаю, что это проблема.У вас всегда должен быть как можно более простой и понятный код и тесты.

0 голосов
/ 09 марта 2019
  1. Переместить большую часть логики домена в модель (вы можете проверить книгу по DDD)
  2. Покрыть модель домена модульными тестами.Таким образом, большинство тестов остаются на уровне модулей, и им не нужно подделывать.
  3. Для каждого элемента функциональности напишите всего несколько высокоуровневых тестов - они проверят, что сериализация, AOP, сопоставление URL-адресов выполнены правильнои все работает с ног до головы.Вы можете использовать MockMvc для этого.

Вы получите Test Pyramid - много быстрых юнит-тестов, не так много высокоуровневых тестов, которые проверяют все это вместе, нетнасмешливые рамки.

0 голосов
/ 14 февраля 2019

макетируйте все с помощью Mockito и проверьте, передается ли объект, полученный из одного макета, другому

Обычно вы извлекаете логику в методы, которые объясняют, что они делают.Если метод использует результат другого вызова метода, я бы посмеялся над вызовом этого метода и заставил бы его вернуть фиксированное значение.

делать интеграционные тесты с контекстом Spring, работающим

У вас обязательно должен быть тест, который проверяет ваш весенний контекст (вам может понадобиться только один)

пропустите тест контроллера, поскольку он не содержит бизнес-журнала

Ваш контроллер долженне содержат бизнес-логики, а скорее делегируют службе, которая выполняет некоторую работу.В Spring есть MockMvc, который позволяет вам тестировать один контроллер за раз (и издеваться над другими компонентами в контексте Spring).Это дает много возможностей для некоторых интересных и полезных тестов.

-

Я предпочитаю макетировать внешние системы.Мне нравится тестировать контроллер и логику в сервисе.Используя MockMvc, я также могу проверить HTTP-ответ.

Допустим, у меня есть трехслойное приложение: Controller -> Service -> Repository (mock this one)

Пример теста с MockMvc:

 @ExtendWith(SpringExtension.class)
 @WebMvcTest(controllers = {CategoryController.class})
 public class CategoryControllerTest {

  @Autowired
  private MockMvc mockMvc;

  @MockBean
  private CategoryRepository repository; // mock database, but validate logic in the service-class

  @Test
  public void create_new_catgory() throws Exception {

    var category = new Category("test");
    given(repository.save(any())).willReturn(category);

    mockMvc.perform(post("/category")
            .content(toJson(new CategoryRequestDto("")))
            .contentType(MediaType.APPLICATION_JSON))
            .andExpect(status().is2xxSuccessful())
            .andExpect(content().json(toJson(category)));

    verify(repository, times(1)).save(any());
  }
}

Пример теста, который является скорее интеграционным тестом(работает база данных in-mem):

@ExtendWith(SpringExtension.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, 
classes = {ShoprApplication.class, TestConfig.class})
@Transactional
public class ProductControllerTest {

  @Autowired TestRestTemplate template;
  @Autowired EntityManagerFactory entityManagerFactory;
  private TestEntityManager em;

  @BeforeEach
  public void configure() {
    em = new TestEntityManager(entityManagerFactory);
  }

  @Test
  public void createProduct() {
    var response = template.postForEntity(
            "/product",
            new Product("Apples", 15.00, new Category("Fruit"), 6),
            Long.class);

    assertEquals(HttpStatus.OK, response.getStatusCode());
    assertTrue(response.getBody() != null && response.getBody().intValue() > 0L);

    var product = em.getEntityManager().find(Product.class, response.getBody());
    assertNotNull(product);
    assertEquals("Apples", product.getName());
}
0 голосов
/ 14 февраля 2019

Ваш контроллер не имеет бизнес-логики, но он принимает и выдает некоторые JSON (или другие данные), которые будут использоваться другими службами (например, веб-интерфейсом).

Вот почему стоит проверитьAPI контракт.

Для этой цели вы можете использовать Spring Could Contract framework.

0 голосов
/ 14 февраля 2019

Если вы хотите делать отличные тесты, единственный способ - это модульные тесты и интеграционные тесты.

Интеграционные тесты запускаются только при необходимости (например: Maven может определить, является ли тест интеграционным или нет).).

И модульные тесты нельзя заменить чем-либо, если вы хотите упругий код.

Хороший способ сделать это, как вы уже догадались, использовать фиктивные движки.(Как и Мокито).

Цель этого - реально контролировать класс всеми возможными способами, без взаимодействия с другими слоями.

Но ВНИМАНИЕ!Это работает, если вы делаете это на ВСЕХ слоях и всех классах.

Если вы работаете над существующим кодом, посмотрите, что сделано, и попробуйте поработать с ним и приложите все усилия.

И помните, что хороший тест не тот, который покрывает больше всего, это тот, который покрывает лучше всего.

0 голосов
/ 14 февраля 2019

Подход, который я видел чаще всего, заключается в следующем:

  • Разработчик, который создает контроллер, создает модульные тесты, используя макеты.
  • Команда QA создает интеграционные тесты с контекстом Spring (и инструментом типа cucumber ).
0 голосов
/ 14 февраля 2019

Я предполагаю, что вы используете загрузочное приложение Spring, вы можете написать свой тестовый класс в тестовом пакете следующим образом:

@RunWith(SpringRunner.class)
@ContextConfiguration(classes = AppConfig.class)
public class TestController {

@Autowired
Controller controller;

@Test
public void test() {
    fail("Not yet implemented");
}

@Test
public void testGroupAlert() throws EntityNotFoundException, Exception {

    Entitydto dto = new Entitydto() //Initialize your Entitydto object
    controller.create(dto);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...