Spring Boot集成测试-在应用程序上下文启动之前模拟@Service?

4
我需要为一个名为X的微服务创建集成测试,该微服务从外部sftp服务器下载、处理和导入csv文件。整个过程由Spring Boot调度程序任务启动,该任务启动Spring Batch作业以处理和导入数据。导入过程由Spring Batch writer执行,它是一个restTemplate存储库(因此它调用另一个名为Y的微服务的post请求)。
我已经成功模拟了sftp服务器,将测试文件放在其中,并且当前的集成测试正在下载该文件。(https://github.com/stefanbirkner/fake-sftp-server-rule/
我的问题是,当应用程序上下文启动时,任务将立即被调度,因此没有触发器(如api调用)。要使整个集成测试工作,我必须模拟通过restTemplate调用外部微服务Y的部分。这个存储库在Spring Batch writer中被调用,并且这个存储库是由一个repositoryFactory创建的,它是一个@Service。该repositoryFactory被注入到Spring Batch配置类中。
我已经尝试在测试类中使用@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
            );
        }
    }

更新于2021年8月27日 我有一个RestConfig @Component,在其中创建了一个新的RestTemplateBuilder。我尝试@MockBean这个组件,以提供一个RestTemplateBuilder Mock,并注入一个MockRestServiceServer对象来捕获出站api调用。但不幸的是,它并没有像预期的那样工作。我是否漏掉了什么?我还尝试创建一个“TestRestController”来触发任务的调度,但它从未提供模拟...


你说,一个任务将被立即安排。是哪个任务?它如何被安排的?有没有@Service/@Controller/@Component可以分享给我们?你能清晰地分离出一个受测系统(SUT)吗? - oliver_t
1个回答

2

我通常直接在测试类中使用@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/mercedes-benz/sechub/blob/3f176a8f4c00b7e8577c9e3bea847ecfc91974c3/sechub-administration/src/test/java/com/daimler/sechub/domain/administration/signup/SignupAdministrationRestControllerMockTest.java

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接