Что это делает: @RunWith (SpringJUnit4ClassRunner.class)

Что делает эта аннотация?
Когда я хотел бы использовать это?
Когда бы я не хотел использовать это?

@RunWith(SpringJUnit4ClassRunner.class)

Я могу найти больше случаев использования этого, когда я Google и не могу найти объяснение 101 того, что эта аннотация должна сообщать мне или когда/почему я буду использовать это?

Ответ 1

Аннотации используются для конфигурирования unit test, требующего инъекции зависимостей Spring.

От Spring Ссылка - 10. Тестирование узла:

10.1 Создание класса unit test

Чтобы unit test выполнял пакетное задание, среда должна загружать задание ApplicationContext. Для его запуска используются две аннотации:

@RunWith (SpringJUnit4ClassRunner.class): указывает, что класс должен использовать средства Spring JUnit.

@ContextConfiguration (locations = {...}): Указывает, какие файлы XML содержат ApplicationContext.

Ответ 2

Если вы используете аннотации, а не файлы XML, то в аннотацию @ContextConfiguration должен быть включен любой класс, для которого вы проводите модульное тестирование, требующее внедрения зависимостей Spring. Например:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = FooManager.class)
class FooManagerTest {

    @Autowired
    FooManager fooManager;

Теперь, когда вы используете fooManager в модульном тесте, для него будет настроен контекст Spring.

Если fooManager автоматически подключается к каким-либо bean-компонентам, то эти классы bean-компонентов также должны быть в аннотации @ContextConfiguration. Поэтому, если бин FooManager автоматически подключается к бину FooReporter:

@ContextConfiguration(classes = {FooManager.class, FooReporter.class})

Если bean-компоненты, которые fooManager автоматически связывают, содержат состояние, то вы, вероятно, захотите сбросить состояние этих bean-компонентов для каждого теста. В этом случае вы можете добавить аннотацию @DirtiesContext к вашему тестовому классу:

@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)

Если fooManager или какой-либо из его компонентов с автопроводкой читает Spring config, то вам нужно добавить список инициализаторов в аннотацию @ContextConfiguration, которая содержит класс ConfigFileApplicationContextInitializer:

@ContextConfiguration(classes = FooManager.class, initializers = ConfigFileApplicationContextInitializer.class)

Ответ 3

Чтобы ответить на вопрос, когда вы хотите и не хотите использовать это часть вопроса.

Когда использовать SpringJUnit4ClassRunner

IMO SpringJUnit4ClassRunner должен использоваться очень экономно. При запуске контейнера Spring для запуска модульного теста возникают значительные накладные расходы.

Обычно я использую SpringJUnit4ClassRunner для проверки:

  • что компоненты вводятся (автоматически подключаются), как и ожидалось
  • эти данные конфигурации вводятся как ожидалось

По крайней мере, у меня всегда есть простой тест SpringJUnit4ClassRunner для проверки работоспособности контейнера Spring. OK

При введении компонентов могут возникнуть проблемы, если аннотация @Qualifier не используется или используется неправильно. Тест проверки работоспособности выявит такие проблемы.

При загрузке конфигурации из нескольких файлов yaml может потребоваться проверить, что карты объединяются, как ожидается, с соответствующими переопределениями.

Когда не следует использовать SpringJUnit4ClassRunner

Я бы не использовал SpringJUnit4ClassRunner для тестирования не связанных со Spring функций в тестируемом коде. Что по моему опыту означает большую часть функциональности.

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

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

import spock.lang.Specification

class FooManagerTest extends Specification {

  FooManager cut

  void createMockFooReporter() {
    FooReporter mockFooReporter = Mock(FooReporter)
    mockFooReporter.fooFormatter = Mock(FooFormatter)
  }

  void setup() {
    cut = new FooManager()
    cut.fooReporter = createMockFooReporter()
  }

  void "Basic test"() {
     // Do a basic test using 'cut'
  }

}

В этом примере у тестируемого класса FooManager есть автопровод FooReporter, который сам содержит автопровод FooFormatter.