@SpringBootTest vs @ContextConfiguration vs @Import в модуле Spring Boot Test - PullRequest
4 голосов
/ 22 июня 2019

Я работаю над проектом Spring Boot. Я пишу код «Unit Test», основанный на «TDD», что немного сложно.

@ SpringBootTest загрузил все BEAN, что привело к увеличению времени тестирования.

Итак, я использовал обозначение класса @ SpringBootTest.

Я прошел тест нормально, но я не уверен, что разница между использованием @ContextConfiguration и использованием @ Import.

Все три варианта работают нормально. Я хочу знать, какой выбор лучший.

@Service
public class CoffeeService {

    private final CoffeeRepository coffeeRepository;

    public CoffeeService(CoffeeRepository coffeeRepository) {
        this.coffeeRepository = coffeeRepository;
    }

    public String getCoffee(String name){
        return coffeeRepository.findByName(name);
    }
}

public interface CoffeeRepository {
    String findByName(String name);
}

@Repository
public class SimpleCoffeeRepository implements CoffeeRepository {

    @Override
    public String findByName(String name) {
        return "mocha";
    }
}

Option 1(SpringBootTest Annotation) - OK  
@RunWith(SpringRunner.class)
@SpringBootTest(classes = {CoffeeService.class, SimpleCoffeeRepository.class})
public class CoffeeServiceTest {

    @Autowired
    private CoffeeService coffeeService;

    @Test
    public void getCoffeeTest() {
        String value = coffeeService.getCoffee("mocha");
        assertEquals("mocha", value);
    }
}


Option 2 (ContextConfiguration Annoation) - OK
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = {SimpleCoffeeRepository.class, CoffeeService.class})
public class CoffeeServiceTest {

    @Autowired
    private CoffeeService coffeeService;

    @Test
    public void getCoffeeTest() {
        String value = coffeeService.getCoffee("mocha");
        assertEquals("mocha", value);
    }
}

Option 3 (Import Annoation) - OK
@RunWith(SpringRunner.class)
@Import({SimpleCoffeeRepository.class, CoffeeService.class})
public class CoffeeServiceTest {

    @Autowired
    private CoffeeService coffeeService;

    @Test
    public void getCoffeeTest() {
        String value = coffeeService.getCoffee("mocha");
        assertEquals("mocha", value);
    }

Ответы [ 2 ]

4 голосов
/ 22 июня 2019

Я думаю, все 3 представленных варианта плохи, если вы собираетесь запустить правильный модульный тест .Модульное тестирование должно быть быстрым, вы должны быть в состоянии запустить сотни из них в секунду или около того (в зависимости от аппаратного обеспечения, конечно, но вы понимаете).Поэтому, когда вы говорите «Я начинаю весну для каждого теста» - это больше не юнит-тест.Запуск Spring для каждого теста - очень дорогая операция.

Интересно то, что ваш код CoffeeService написан так, что он отлично тестируется: просто используйте какую-нибудь библиотеку, такую ​​как Mockito, для насмешки над классом репозитория, и выможет проверить сервисную логику вообще без пружины.Вам не понадобится никакой весенний бегун, любые весенние аннотации.Вы также увидите, что эти тесты работают намного быстрее.

  class MyServiceTest {

        @Test
        public void test_my_service_get_coffee_logic() {

               // setup
               CoffeeRepository repo = Mockito.mock(CoffeeRepository.class);
               Mockito.when(repo.findByName("mocha")).thenReturn("coffeeFound");

               CoffeeService underTest = new CoffeeService(repo);


               // when:
               String actualCoffee  =  underTest.getCoffee("mocha");

               // then:
               assertEquals(actualCoffee, "coffeeFound");
        }
  }

Теперь о библиотеке пружинных тестов

Вы можете думать об этом как о способе тестирования кода, который требует некоторых соединений с другимикомпоненты и его проблематично макетировать.Это своего рода интеграционный тест внутри той же JVM.Все способы, которые вы представили, запускают контекст приложения, и это очень сложная вещь на самом деле, на youtube есть целые сессии о том, что на самом деле происходит во время запуска контекста приложения - хотя, за рамками вопроса,Дело в том, что для запуска запуска контекста требуется время

@SpringBootTest идет дальше и пытается имитировать процессы, добавленные средой Spring Boot для создания контекста: решает, что сканировать на основе структур пакета, загружает внешние конфигурациииз предопределенных расположений при необходимости запускаются автоконфигурационные пускатели и т. д. и т. д.

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

Например, если вы тестируете остальные контроллеры (что вы правильно разместили все аннотации), вероятно, вам не нужно запускать соединения с БД.

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

Обычно эти ограничения применяются к «слоям», а не к отдельным компонентам.(слои = уровень покоя, уровень данных и т. д.).

Второй и третий методы фактически одинаковы, они представляют собой разные способы «фильтрации» контекста приложения, сохраняя только необходимые компоненты.

Обновление:

Поскольку вы уже провели сравнение производительности методов:

Модульный тест = очень быстрый тест, его цель - проверить код, который выВы написали (или один из ваших коллег, конечно) Так что если вы запускаете Spring, это автоматически означает относительно медленный тест.Поэтому, чтобы ответить на ваш вопрос

Может ли использование @ContextConfiguration быть "модульным тестом"

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

Обычно мы не запускаем только один класс с Spring Framework.Какая польза от его запуска внутри контейнера Spring, если вы хотите протестировать код только одного класса (модуля)?Да, в некоторых случаях это может быть пара классов, но не десятки или сотни.

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

Теперь по вашим вопросам

@ ContextConfiguration против @SpringBootTest технические различия.

@SpringBootTest имеет отношение к делутолько если у вас есть приложение Spring Boot.Эта среда использует Spring под капотом, но, в двух словах, поставляется со многими предопределенными рецептами / практиками того, как написать «инфраструктуру» приложения: - управление конфигурацией, - структура пакета, - подключаемость - ведение журнала - интеграция базы данных и т. Д..

Итак, Spring Boot устанавливает четко определенные процессы для работы со всеми вышеупомянутыми элементами, и если вы хотите запустить тест, имитирующий приложение весенней загрузки, вы используете аннотацию @SpringBootTest. В противном случае (или если у вас только приложение с пружинным приводом, а не пружинный башмак) - не используйте его вообще.

@ContextConfiguration это совсем другая вещь. В нем просто говорится, какие bean-компоненты вы хотели бы использовать в приложении, управляемом Spring (это также работает с загрузкой Spring)

Является ли «модульный тест» правильным способом использования @ContextConfiguration? Или нет?

Как я уже сказал - все, что связано с весенним тестированием, предназначено только для интеграционного тестирования, так что нет, это неправильный способ использования в модульных тестах. Для юнит-тестов используйте то, что совсем не использует пружину (например, mockito для насмешек и обычный тест джунта без пружинного бегуна).

0 голосов
/ 22 июня 2019

как @MarkBramnik говорит, что если вы намереваетесь написать модульный тест, вы должны смоделировать другие компоненты, которые используют тот, который вы тестируете. @SpringBootTest рекомендуется, если вы хотите написать интеграционный тест, имитирующий процесс приложения. @ContextConfiguration используется, когда вы @Autowired компонента в своем модульном тесте, и вы должны установить для конфигурации этот класс или класс, в котором вы создали компонент

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...