你不需要模拟任何东西,只需在测试中注入你的类,你的配置就会为你提供bean
ThingDoerinator
类和测试用例将像调用
ThingDoerinator. doStuff()
方法来自某个控制器或其他服务。
TL;博士
我认为,您会混淆运行整个spring上下文的测试和仅测试我们编写的函数/逻辑而不运行整个上下文的单元测试。编写单元测试是为了测试函数,如果该函数在给定的输入集下按预期运行。
理想的单元测试是不需要模拟的,我们只需传递输入,它就会产生输出(纯函数),但在实际应用中,情况往往并非如此,当我们与应用程序中的其他对象交互时,我们可能会发现自己处于这种情况。由于我们不打算测试对象交互,而是关心我们的功能,因此我们模拟了对象行为。
看来,你对单元测试没有问题,所以你可以嘲笑你的
List
你的测试课上有豆子
ThingDoerinator
并将它们注入到测试用例中,测试用例就工作得很好。
现在,你想对…做同样的事情
@SpringBootTest
,所以我举了一个假设的例子来证明,当
@springBootTest
将加载整个上下文以运行单个测试用例。
假设我有一项服务
FizzService
它有一种方法
getFizzes()
@Service
public class FizzService {
private final List<Fizz> fizzes;
public FizzService(List<Fizz> fizzes) {
this.fizzes = fizzes;
}
public List<Fizz> getFizzes() {
return Collections.unmodifiableList(fizzes);
}
}
在这里使用构造函数注入
1.
现在,我的
List<Fizzes
将通过配置(类似于您的情况)创建,并被告知根据需要向spring上下文注入它们。
@Profile("dev")
@Configuration
public class FizzConfig {
@Bean
public List<Fizz> allFizzes() {
return asList(Fizz.of("Batman"), Fizz.of("Aquaman"), Fizz.of("Wonder Woman"));
}
}
暂时忽略@Profile注释
2.
现在,我将进行春季启动测试,以检查此服务是否会给出相同的列表
fizzes
我向上下文提供了它何时投入生产
@ActiveProfiles("test")
@SpringBootTest
class FizzServiceTest {
@Autowired
private FizzService service;
@Test
void shouldGiveAllTheFizzesInContext() {
List<Fizz> fizzes = service.getFizzes();
assertThat(fizzes).isNotNull().isNotEmpty().hasSize(3);
assertThat(fizzes).extracting("name")
.contains("Wonder Woman", "Aquaman");
}
}
现在,当我运行测试时,我看到它是有效的,那么它是如何工作的,让我们来了解一下。所以当我用
@SpringBootTest
注释,它将像在嵌入式服务器的生产环境中一样运行。
因此,我之前创建的配置类将被扫描,然后在那里创建的bean将被加载到上下文中,我的服务类也将被注释为
@Service
注释,这样它也会被加载,spring会识别它需要什么
List<Fizz>
然后它会查看它的上下文,如果它有那个bean,它就会找到并注入它,因为我们是从配置类提供它的。
在我的测试中,我正在自动布线服务类,所以spring会注入
Fizz服务
对象它有,它会有我的
列表<Fizz>
也可以(在前面的段落中解释)。
因此,您不必做任何事情,如果您定义了配置,并且这些配置正在加载并在生产环境中工作,那么这些配置应该在您的春季引导测试中以相同的方式工作,除非您有一些比默认春季引导应用程序更自定义的设置。
现在,让我们来看一个案例,当你可能想要不同的
列表<Fizz>
在测试中,在这种情况下,您必须排除生产配置,并包含一些测试配置。
在我的例子中,我会创建另一个
FizzConfig
在我的测试文件夹中,并为其添加注释
@TestConfiguration
@TestConfiguration
public class FizzConfig {
@Bean
public List<Fizz> allFizzes() {
return asList(Fizz.of("Flash"), Fizz.of("Mera"), Fizz.of("Superman"));
}
}
我会稍微改变一下我的测试
@ActiveProfiles("test")
@SpringBootTest
@Import(FizzConfig.class)
class FizzServiceTest {
@Autowired
private FizzService service;
@Test
void shouldGiveAllTheFizzesInContext() {
List<Fizz> fizzes = service.getFizzes();
assertThat(fizzes).extracting("name")
.contains("Flash", "Superman");
}
}
使用不同的配置文件进行测试,因此使用@ActiveProfile注释,否则默认配置文件集为
dev
这将给生产带来压力
FizzConfig
类,因为它会扫描所有包(
2.
记住
@Profile
上面的注释,这将确保早期的配置只在product/dev-env中运行)。这是我的应用程序。yml可以让它工作。
spring:
profiles:
active: dev
---
spring:
profiles: test
另外,请注意,我正在导入我的
FizzConfiguration
具有
@Import
在这里添加注释,您也可以使用
@SpringBootTest(classes = FizzConfig.class)
.
所以,您可以看到测试用例的值与生产代码中的值不同。
编辑
如前所述,由于您不希望测试在此测试中连接到数据库,因此您必须禁用spring数据JPA的自动配置,默认情况下spring boot会这样做。
3.
您可以创建另一个配置类,如下所示
@Configuration
@EnableAutoConfiguration(exclude = {DataSourceAutoConfiguration.class, DataSourceTransactionManagerAutoConfiguration.class, HibernateJpaAutoConfiguration.class})
public TestDataSourceConfiguration {}
然后把它和你的
@SpringBootTest
,这将禁用默认数据库设置,然后您将剩下最后一块拼图,即您在类中可能进行的任何存储库交互。
因为现在,您的测试用例中没有自动配置,这些存储库只是接口,您可以在测试类中轻松模拟它们
@ActiveProfiles("test")
@SpringBootTest
@Import(FizzConfig.class, TestDataSourceConfiguration.class)
class FizzServiceTest {
@MockBean
SomeRepository repository;
@Autowired
private FizzService service;
@Test
void shouldGiveAllTheFizzesInContext() {
Mockito.when(repository.findById(any(), any()).thenReturn(Optional.of(new SomeObject));
List<Fizz> fizzes = service.getFizzes();
assertThat(fizzes).extracting("name")
.contains("Flash", "Superman");
}
}
1.
建议在生产代码中使用构造函数注入,而不是“@Autowired”,如果该类真的需要依赖关系才能工作,所以如果可能的话,请避免使用它。
3.
请注意,您只能在测试包中创建此类配置或用一些配置文件标记,不要在生产包中创建,否则它将为您正在运行的代码禁用数据库。