有没有办法在 Spring Boot 中测试没有 Web 或持久层的嵌套对象?

Is there a way to test nested objects without the web or persistence layer in Spring Boot?

我正在使用 JUnit5 测试 Spring 引导应用程序。我想测试一个 @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";
    }

}

使用的测试class:

@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());
    }
    
}

如果我 运行 测试 class 作为 @SpringBootTest,上面的方法有效,但是这会加载完整的应用程序并且非常慢。我还想避免 @WebMvcTest 因为我不需要网络服务器,或者 @DataJpaTest 因为我不需要持久性。我不想模拟 SubService,因为它包含我想与 MainService.

一起测试的功能

我尝试了以下方法:

有没有办法在不加载 Web 服务器或持久层的情况下使用 spring 依赖注入系统,或者不使用 Spring 测试但允许 'nested' 依赖注入?

您可以使用分析(即 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(classes = 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
    }

}

这将仅加载 class TestConfiguration 中定义的 beans。 注意:由于您的问题更多是关于如何避免加载整个应用程序,因此我的回答主要集中在这一点上。上面的方法将完成工作,但我更喜欢在任何给定的一天使用构造函数连接而不是任何其他依赖注入模式,它更容易维护和测试(比如你想要模拟的情况)。