【问题标题】:Spring Boot Integration Testing - Mocking @Service before Application Context Starts?Spring Boot 集成测试 - 在应用程序上下文启动之前模拟 @Service?
【发布时间】:2021-08-26 14:37:11
【问题描述】:

我必须为从外部 sftp 服务器下载、处理和导入 csv 文件的微服务 X 创建一个集成测试。整个过程由一个 spring boot 调度程序任务启动,该任务启动一个 spring 批处理作业来处理和导入数据。导入过程由 spring 批处理编写器完成,它是一个 restTemplate 存储库(因此它调用另一个微服务 Y 的 post 请求)。

我已经设法模拟了 sftp 服务器,在其上放置了一个测试文件,当前的集成测试正在下载该文件。 (https://github.com/stefanbirkner/fake-sftp-server-rule/)

我的问题是,任务将在应用程序上下文启动时立即安排,因此没有像 api 调用这样的触发器。为了让整个集成测试正常工作,我必须模拟通过 restTemplate 调用调用外部微服务 Y 的部分。这个存储库在 spring 批处理编写器中被调用,这个存储库是由一个 repositoryFactory 创建的,它是一个 @Service。 repositoryFactory 是在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 @Component 在其中创建了一个新的 RestTemplateBuilder。我尝试@MockBean 这个组件来传递一个 RestTemplateBuilder Mock 并注入一个 MockRestServiceServer 对象来捕获传出的 api 调用。但不幸的是,它不能像方面那样工作。我错过了什么吗?我还尝试创建一个“TestRestController”来触发任务的调度,但它从不提供模拟......

【问题讨论】:

  • 你说,一个任务会马上安排。哪个任务?它是如何安排的?有没有可以与我们分享的@Service/@Controller/@Component?您能否清楚地隔离被测系统 (SUT)?

标签: java spring spring-boot mocking integration-testing


【解决方案1】:

我通常在我的测试类中直接使用@MockBean,并直接在那里注入专用(但模拟的)存储库,而不是在测试配置中创建它。我还通过@ContextConfiguration 添加了测试配置类,以便在当前测试上下文中加载它。

在我的测试中,我只是以标准方式使用 mockito,并根据需要为专用测试方法准备模拟部分。

这里是一个例子 sn-p:

// ... 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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-08-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-08-31
    • 1970-01-01
    • 2020-01-15
    相关资源
    最近更新 更多