要在Spring Boot应用程序中测试组件/ bean,the testing part of the Spring Boot documentation提供了很多信息和多种方式:
@Test
,@SpringBootTest
,@WebMvcTest
,@DataJpaTest
以及许多其他方式。
为什么提供这么多方式?
如何决定青睐的方式?
我是否应该将用Spring Boot测试注释(例如@SpringBootTest
,@WebMvcTest
,@DataJpaTest
注释的测试类视为集成测试?
PS:之所以创建此问题,是因为我注意到许多开发人员(甚至经验丰富的开发人员)使用注解而不是使用注解并没有带来后果。
答案 0 :(得分:22)
为不装载Spring容器就无法直接测试的组件编写部分集成测试/ slicing unit test ,例如与JPA,控制器,REST客户端,JDBC等相关的组件。 (在本地和CI构建中运行它们)
为一些带来价值的高级组件编写一些完整的集成测试(端到端测试)(在CI构建中运行它们)。
使用Spring的一般方法可以在集成测试中测试任何组件,并且只有某些类型的组件适合进行整体测试(无容器)。
但是请注意,无论有没有弹簧,单一测试和集成测试都不是相反的,而是互补的。
在应用程序中使用Spring Boot并不意味着您需要为运行的任何测试类加载Spring容器。
在编写不需要来自Spring容器的任何依赖项的测试时,您没有在测试类中使用/加载Spring。
代替使用Spring,您将自己实例化要测试的类,并在需要时使用模拟库将受测实例与其依赖项隔离。
之所以采用这种方法,是因为它速度快并且有利于隔离测试的组件。
例如,可以在没有Spring的情况下测试一个注解为Spring服务的FooService
,它执行一些计算并且依靠FooRepository
来检索一些数据:
@Service
public class FooService{
private FooRepository fooRepository;
public FooService(FooRepository fooRepository){
this.fooRepository = fooRepository;
}
public long compute(...){
List<Foo> foos = fooRepository.findAll(...);
// core logic
long result =
foos.stream()
.map(Foo::getValue)
.filter(v->...)
.count();
return result;
}
}
您可以模拟FooRepository
并对FooService
的逻辑进行单元测试。
使用JUnit 5和Mockito时,测试类如下所示:
import org.mockito.junit.jupiter.MockitoExtension;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
@ExtendWith(MockitoExtension.class)
class FooServiceTest{
FooService fooService;
@Mock
FooRepository fooRepository;
@BeforeEach
void init{
fooService = new FooService(fooRepository);
}
@Test
void compute(){
List<Foo> fooData = ...;
Mockito.when(fooRepository.findAll(...))
.thenReturn(fooData);
long actualResult = fooService.compute(...);
long expectedResult = ...;
Assertions.assertEquals(expectedResult, actualResult);
}
}
您识别出要测试的代码,该代码与Spring容器没有任何依赖关系,因为组件/方法不使用Spring功能来执行其逻辑。
在上一个示例中,FooService
执行一些不需要执行Spring的计算和逻辑。
实际上,无论有没有容器,compute()
方法都包含我们要声明的核心逻辑。
相反,如果没有Spring,您将很难测试FooRepository
,因为Spring Boot为您配置数据源,JPA上下文,并检测FooRepository
接口以为其提供默认实现和其他多种功能。
测试控制器(静止或MVC)的方法相同。
没有Spring,如何将控制器绑定到端点?在没有Spring的情况下,控制器如何解析HTTP请求并生成HTTP响应?根本做不到。
编写端到端测试需要使用应用程序的整个配置和bean加载容器。
实现@SpringBootTest
的方法是:
该注释通过创建在您的 通过SpringApplication进行测试
您可以通过这种方式使用它来进行测试,而无需进行任何模拟:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.junit.jupiter.api.Test;
@SpringBootTest
public class FooTest {
@Autowired
Foo foo;
@Test
public void doThat(){
FooBar fooBar = foo.doThat(...);
// assertion...
}
}
但是如果可以的话,您也可以模拟容器中的一些bean:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.boot.test.context.SpringBootTest;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
@SpringBootTest
public class FooTest {
@Autowired
Foo foo;
@MockBean
private Bar barDep;
@Test
public void doThat(){
Mockito.when(barDep.doThis()).thenReturn(...);
FooBar fooBar = foo.doThat(...);
// assertion...
}
}
请注意,模拟的区别在于您想模拟Bar
类的纯实例(org.mockito.Mock
注释)和想要模拟Spring上下文的Bar
bean( org.springframework.boot.test.mock.mockito.MockBean
注释)。
加载完整的spring上下文需要时间。因此,您应该对@SpringBootTest
保持谨慎,因为这可能会使单元测试的执行时间过长,并且通常您不希望强烈减慢开发人员计算机上的本地构建以及对测试至关重要的测试反馈为开发人员编写愉快而高效的文件。
这就是通常在开发人员的计算机上不执行“慢速”测试的原因。
因此,您应该使它们成为集成测试(在测试类的命名中使用IT
后缀而不是Test
后缀),并确保仅在连续集成构建中执行这些测试。
但是,由于Spring Boot对应用程序中的许多内容起作用(其余控制器,MVC控制器,JSON序列化/反序列化,持久性等),因此您可以编写许多仅在CI构建上执行的单元测试,而并非如此也可以。
仅在CI构建上执行端到端测试是可以的,但仅在CI构建上执行持久性,控制器或JSON测试也完全不可行。
确实,开发人员构建会很快,但是作为缺点,在本地执行测试只能检测到可能的回归中的一小部分...
为了避免这种警告,Spring Boot提供了一种中间方式:部分集成测试或切片测试(如他们所说的):下一点。
如“识别可以简单测试(无弹簧)的测试)”所述,某些组件只能在运行的容器中进行测试。
但是,为什么要使用@SpringBootTest
来加载应用程序的所有bean和配置,而只需要加载一些特定的配置类和bean来测试这些组件呢?
例如,为什么要加载完整的Spring JPA上下文(bean,配置,内存数据库等)以统一测试控制器?
相反,为什么要加载与Spring控制器关联的所有配置和Bean,以统一测试JPA存储库?
Spring Boot用slice testing feature解决了这一点。
这些速度没有普通单元测试(没有容器)快,但实际上比加载整个上下文快得多。 因此通常可以在本地计算机上执行它们。
每个切片测试版本都会加载一组非常有限的自动配置类,您可以根据需要根据需要进行修改。
一些常见的切片测试功能:
要测试对象JSON序列化和反序列化是否正常 如预期的那样,您可以使用@JsonTest批注。
要测试Spring MVC控制器是否按预期工作,请使用
@WebMvcTest
注释。
要测试Spring WebFlux控制器是否按预期工作,您可以 可以使用
@WebFluxTest
注释。
您可以使用
@DataJpaTest
批注来测试JPA应用程序。
Spring Boot还为您提供了许多其他切片功能。
请参阅the testing part of the documentation,以获取更多详细信息。