Тестирование интеграции с весенней загрузкой — Издевательство над @Service перед запуском контекста приложения?

#java #spring #spring-boot #mocking #integration-testing

Вопрос:

Мне нужно создать интеграционный тест для микросервиса X, который загружает, обрабатывает и импортирует csv-файлы с внешних sftp-серверов. Весь процесс запускается задачей планировщика загрузки spring, которая запускает пакетное задание spring для обработки и импорта данных. Процесс импорта выполняется программой записи пакетов spring, которая является репозиторием RestTemplate (поэтому она вызывает запросы post к другому микросервису Y).

Мне уже удалось поиздеваться над sftp-сервером, поместив на него тестовый файл, и текущий интеграционный тест загружает файл. (https://github.com/stefanbirkner/fake-sftp-server-rule/)

Моя проблема в том, что задача будет запланирована сразу же при запуске контекста приложения, поэтому нет триггера, подобного вызову api. Чтобы весь интеграционный тест работал, я должен имитировать ту часть, где внешний микросервис Y вызывается с помощью вызова RestTemplate. Этот репозиторий вызывается в программе записи пакетов spring, и этот репозиторий создается репозиторием, который является службой@. Репозиторий Factory вводится в класс конфигурации пакета spring.

Я уже пытался использовать аннотацию @MockBean в тестовом классе, а также в отдельной тестовой конфигурации, где я издеваюсь над функцией create() фабрики для доставки макета репозитория. Но в какой-то момент это не работает, и он по-прежнему доставляет исходный объект, что приводит к прерыванию задания импорта.

Я также пытался использовать библиотеку WireMock, но и в этом случае она не улавливает никаких вызовов api и в какой-то момент приводит к прерыванию сокета sftp. (?)

Я надеюсь, что кто-нибудь сможет мне помочь.

Текущий тест:

 @NoArgsConstructor
@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@ContextConfiguration(classes = {JsonHalConfig.class})
@TestPropertySource(locations = "classpath:application-test.properties")
@TestMethodOrder(MethodOrderer.MethodName.class)
public class ImportIT {

    @ClassRule
    public static final FakeSftpServerRule sftpServer = new FakeSftpServerRule();
    private static final String PASSWORD = "password";
    private static final String USER = "username";
    private static final int PORT = 14022;

    @BeforeClass
    public static void beforeClass() throws IOException {
        URL resource = getTestResource();
        if (resource != null) {
            sftpServer.setPort(PORT).addUser(USER, PASSWORD);
            sftpServer.createDirectories("/home/username/it-space/IMPORT", "/home/username/it-space/EXPORT");
            sftpServer.putFile("/home/username/it-space/IMPORT/INBOX/testFile.csv",
                    resource.openStream());
        } else {
            throw new IOException("Failed to get test resources");
        }
    }

    private static URL getTestResource() {
        return ImportIT.class.getClassLoader().getResource("testFile.csv");
    }

    @Test
    public void test_A_() throws IOException, RepositoryException {
        assertTrue(true);
    }
}

 

Я попытался следовать классам конфигурации

(включено в @ContextConfiguration)

 @Configuration/@TestConfiguration
public class RepositoryTestConfig {
    @Bean
    @Primary
    public IRepositoryFactory repositoryFactory() {
        IRepositoryFactory repositoryFactory = mock(IRepositoryFactory.class);
        IRepository repository = mock(IRepository.class);
        when(repositoryFactory.create(anyString())).thenReturn(repository);
        return repositoryFactory;
    }
}
 

(как статический класс в тестовом классе)

     @TestConfiguration/@Configuration
    public static class RepositoryTestConfig {
        @MockBean
        private IRepositoryFactory repositoryFactory;

        @PostConstruct
        public void initMock(){
            IRepository repository = mock(IRepository.class);
            Mockito.when(repositoryFactory.create(anyString())).thenReturn(
                    repository
            );
        }
    }
 

ОБНОВЛЕНИЕ 27.08.2021
У меня есть компонент RestConfig @, в котором создается новый RestTemplateBuilder. Я попытался @MockBean этот компонент для доставки макета RestTemplateBuilder и ввел объект MockRestServiceServer для перехвата исходящих вызовов api. Но, к сожалению, это работает не так, как предполагалось. Я что-то упускаю? Я также попытался создать «TestRestController», чтобы запустить планирование задачи, но он никогда не выдает макет…

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

1. Вы говорите, что задание будет запланировано немедленно. Какая задача? Как это запланировано? Есть ли какой-либо компонент @Service/@Controller/@, которым вы можете поделиться с нами? Можете ли вы четко изолировать Тестируемую систему (SUT)?

Ответ №1:

Обычно я использую @MockBean непосредственно в своих тестовых классах и внедряю выделенный (но издевательский) репозиторий непосредственно туда, а не создаю его внутри тестовой конфигурации. Я также добавляю класс конфигурации теста @ContextConfiguration , чтобы он загружался в текущем контексте теста.

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

Вот пример фрагмента:

 // ... do some imports ...
@RunWith(SpringRunner.class)
@ContextConfiguration(classes= {XYZSomeWantedClazz.class, DemoXYZMockTest.SimpleTestConfiguration.class})
@ActiveProfiles({Profiles.TEST})
public class DemoXYZMockTest {
   //...
   @MockBean
   private DemoRepository mockedDemoRepository;
   // ...
   @Test
   public void testMethodName() throws Exception{
       /* prepare */
       List<WantedEntityClazz> list = new ArrayList<>();
       // add your wanted data to your list

       // apply to mockito:
       when(mockedDemoRepository.findAll()).thenReturn(list);

       /* execute */
       // ... execute the part you want to test...
 
       /* test */
       // ... test the results after execution ...

   }


   @TestConfiguration
   @Profile(Profiles.TEST)
   @EnableAutoConfiguration
   public static class SimpleTestConfiguration{
      // .. do stuff if necessary or just keep it empty
   }

}
 

Для полного (старого Junit4) рабочего примера теста, пожалуйста, взгляните на:
https://github.com/Daimler/sechub/blob/3f176a8f4c00b7e8577c9e3bea847ecfc91974c3/sechub-administration/src/test/java/com/daimler/sechub/domain/administration/signup/SignupAdministrationRestControllerMockTest.java