【问题标题】:Is there a way to test nested objects without the web or persistence layer in Spring Boot?有没有办法在 Spring Boot 中测试没有 Web 或持久层的嵌套对象?
【发布时间】:2020-08-31 13:38:27
【问题描述】:

我正在使用 JUnit5 来测试 Spring Boot 应用程序。我想测试一个使用@Autowired 字段的@Service 对象。我想模拟我的测试对象间接使用的另一个 @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 的直接字段,这不起作用。

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

【问题讨论】:

  • 您要测试哪个类?为什么在测试MainService的时候不能mock SubService,单独为SubService写单元测试?
  • @msfoster 我正在测试 MainService。从技术上讲,我可以模拟 SubService,但我不想这样做,因为它在底层实现中没有多大意义(模拟只是 SubService 中代码的副本)。
  • 存根很简单,适合测试用例。为什么要复制实现?
  • 如果你提供了一个你想测试的方法的例子,你会更容易得到帮助:)
  • 我看到您已经在测试中使用了 Mockito 依赖项,并且您想避免使用 SpringBootTest,为什么不使用 Mockito 的 @Mock 和 @InjectMock 来对您需要的内容进行单元测试?

标签: spring spring-boot junit 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(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
    }

}

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

【讨论】:

    猜你喜欢
    • 2021-06-26
    • 1970-01-01
    • 2013-04-26
    • 1970-01-01
    • 2021-10-24
    • 2022-01-24
    • 2019-03-19
    • 2016-02-02
    • 2018-10-17
    相关资源
    最近更新 更多