Есть ли способ протестировать вложенные объекты без веб-слоя или уровня сохраняемости в Spring Boot?

#spring #spring-boot #junit #mockito

#весна #spring-boot #junit #mockito

Вопрос:

Я использую JUnit5 для тестирования приложения Spring Boot. Я хочу протестировать @Service объект, который использует @Autowired поля. Я хотел бы издеваться над другим @Service объектом, который косвенно используется моим тестовым объектом. Конкретно, у меня есть следующая (сильно упрощенная) настройка:

Тестируемый объект:

 @Service
public class MainService {
    
    private @Autowired SubService subService;
    
    public String test() {
        return subService.test();
    }

}
  

SubService :

 @Service
public class SubService {
    
    private @Autowired StringService stringService;

    public String test() {
        return stringService.test();
    }

}
  

StringService :

 @Service
public class StringService {

    public String test() {
        return "Real service";
    }

}
  

Используемый тестовый класс:

 @SpringBootTest
public class MainServiceTest {

    private @Autowired MainService mainService;
    private @MockBean StringService stringService;
    
    @BeforeEach
    public void mock() {
        Mockito.when(stringService.test()).thenReturn("Mocked service");
    }
    
    @Test
    public void test() {
        assertEquals("Mocked service", mainService.test());
    }
    
}
  

Вышеуказанное работает, если я запускаю тестовый класс как @SpringBootTest , но это загружает все приложение и работает очень медленно. Я также хочу избежать, @WebMvcTest поскольку мне не нужен веб-сервер или @DataJpaTest поскольку мне не нужна сохраняемость. Я не хочу издеваться SubService , поскольку он содержит функциональность, которую я хочу протестировать вместе с MainService .

Я попробовал следующее:

  • @ExtendWith(SpringExtension.class) => бросает NoSuchBeanDefinitionException , похоже, что автоматическое подключение в этом случае не работает
  • @ExtendWith(MockitoExtension.class) и использование @InjectMocks и @Mock вместо аннотаций Spring => поскольку StringService это не прямое поле MainService тестируемого, это не работает.

Есть ли способ использовать систему внедрения зависимостей Spring без загрузки веб-сервера или уровня сохранения или, альтернативно, не использовать тесты Spring, но разрешить внедрение «вложенных» зависимостей?

Комментарии:

1. Какой класс вы пытаетесь протестировать? Почему вы не можете имитировать SubService при тестировании MainService и писать модульные тесты для SubService отдельно?

2. @msfoster Я тестирую MainService. Технически я могу имитировать SubService, но я предпочитаю этого не делать, поскольку это не имеет большого смысла в базовой реализации (макет будет просто копией кода в SubService).

3. Заглушки просты, подходят для тестового примера. Зачем вам копировать реализацию?

4. Если вы приведете пример метода, который вы хотели бы протестировать, вам будет проще помочь 🙂

5. Я вижу, что вы уже используете зависимость Mockito для своего теста, и вы хотите избежать SpringBootTest , почему бы тогда не использовать @Mock и @InjectMock Mockito для простого модульного тестирования того, что вам нужно?

Ответ №1:

Вы можете использовать профилирование (т. Е. Spring @Profile ), чтобы избежать загрузки всего приложения. Это будет выглядеть примерно так, как показано ниже:

 @Profile("test")
@Configuration
public class TestConfiguration {
    @Bean
    public MainService mainService() {
        return new MainService();
    }

    @Bean
    public SubService subService() {
        return new SubService();
    }
    // mock the StringService
    @Bean
    public StringService stringService() {
        return Mockito.mock(StringService.class);
    }
 
}
  

затем используйте этот профиль с помощью `@SpringBootTest(классы = TestConfiguration.class ), это будет выглядеть примерно так, как показано ниже:

 @ActiveProfiles("test")
@SpringBootTest(classes = TestConfiguration.class)
class MainServiceTest {

    @Autowired
    private MainService mainService;

    @Test
    public void test() {
        // configure behavior using apis like when(), basically however you 
        // want your mock to behave
    }

}
  

При этом будут загружены только компоненты, определенные в классе TestConfiguration .
ПРИМЕЧАНИЕ: Поскольку ваш вопрос больше касается того, как избежать загрузки всего приложения, я ответил, сосредоточившись на этом. Приведенный выше подход позволит выполнить работу, но я бы предпочел подключение конструктора любому другому способу внедрения зависимостей в любой день, его проще поддерживать и тестировать (например, случаи, когда вы хотите издеваться).