我正在做一个Spring Boot项目。我正在基于“ TDD”编写“单元测试”代码,这有点困难。
@SpringBootTest加载了所有的BEAN,这导致更长的测试时间。
因此,我使用了@SpringBootTest的类名称。
我已正常完成测试,但不确定使用@ContextConfiguration与使用@Import之间的区别。
这三个选项均正常运行。我想知道哪种选择是最好的。
@Service
public class CoffeeService {
private final CoffeeRepository coffeeRepository;
public CoffeeService(CoffeeRepository coffeeRepository) {
this.coffeeRepository = coffeeRepository;
}
public String getCoffee(String name){
return coffeeRepository.findByName(name);
}
}
public interface CoffeeRepository {
String findByName(String name);
}
@Repository
public class SimpleCoffeeRepository implements CoffeeRepository {
@Override
public String findByName(String name) {
return "mocha";
}
}
Option 1(SpringBootTest Annotation) - OK
@RunWith(SpringRunner.class)
@SpringBootTest(classes = {CoffeeService.class, SimpleCoffeeRepository.class})
public class CoffeeServiceTest {
@Autowired
private CoffeeService coffeeService;
@Test
public void getCoffeeTest() {
String value = coffeeService.getCoffee("mocha");
assertEquals("mocha", value);
}
}
Option 2 (ContextConfiguration Annoation) - OK
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = {SimpleCoffeeRepository.class, CoffeeService.class})
public class CoffeeServiceTest {
@Autowired
private CoffeeService coffeeService;
@Test
public void getCoffeeTest() {
String value = coffeeService.getCoffee("mocha");
assertEquals("mocha", value);
}
}
Option 3 (Import Annoation) - OK
@RunWith(SpringRunner.class)
@Import({SimpleCoffeeRepository.class, CoffeeService.class})
public class CoffeeServiceTest {
@Autowired
private CoffeeService coffeeService;
@Test
public void getCoffeeTest() {
String value = coffeeService.getCoffee("mocha");
assertEquals("mocha", value);
}
Run Code Online (Sandbox Code Playgroud)
Har*_*der 16
@MarkBramnik 的答案是我读过的关于 Spring 测试的最简单的答案。从 Spring 官方文档来看,一切都已说明,您的测试大约有 2 个类别:
Unit Tests:不需要加载 Spring 上下文。此时,您不需要任何Spring TestContext Framework注释。您可以简单地使用JUnit、TestNG、Mockito等...组成应用程序的 POJO 应该可以在 JUnit 或 TestNG 测试中进行测试,并使用 new 运算符实例化对象,而无需使用 Spring 或任何其他容器。您可以使用模拟对象(与其他有价值的测试技术结合使用)来单独测试您的代码
Integration Tests:这需要您加载部分或全部上下文,因此您使用所有需要的 Spring 注释然而,对于某些单元测试场景,Spring框架提供了模拟对象和测试支持类,本章将对此进行描述
来自 Spring文档:
与传统 Java EE 开发相比,依赖注入应该使您的代码对容器的依赖更少。组成应用程序的 POJO 应该可以在 JUnit 或 TestNG 测试中进行测试,并使用 new 运算符实例化对象,而无需使用 Spring 或任何其他容器。您可以使用模拟对象(结合其他有价值的测试技术)来单独测试您的代码。如果您遵循Spring 的架构建议,那么代码库的干净分层和组件化将有助于更轻松地进行单元测试。例如,您可以通过存根或模拟 DAO 或存储库接口来测试服务层对象,而无需在运行单元测试时访问持久数据。
真正的单元测试通常运行得非常快,因为不需要设置运行时基础设施。强调真正的单元测试作为开发方法的一部分可以提高您的生产力。您可能不需要测试章节的这一部分来帮助您为基于 IoC 的应用程序编写有效的单元测试。然而,对于某些单元测试场景,Spring 框架提供了模拟对象和测试支持类,本章将对此进行描述。
如果您的应用程序基于 Spring 推荐的架构(存储库 > 服务 > 控制器 > 等...),您应该具有以下规则(在我看来):
@DataJpaTest测试切片的注释。@WebMvcTest测试切片注释或使用JUnit和Mockito。在这里,您将在这两种情况下嘲笑您的服务@ExtendWith(SpringExtension.class)和@ContextConfiguration/@Import或@SpringJUnitWebConfig两者的组合。@DataLdapTest来模拟它。WireMock@SpringBootTest如果您的目的是进行适当的单元测试,那么我认为提出的所有3个选项都是不好的。单元测试必须非常快,您应该能够在一秒钟左右的时间里运行数百个(当然,这取决于硬件,但是您知道了)。因此,一旦您说出“我为每项测试开始测试”,它就不再是单元测试了。每个测试的启动弹簧是非常昂贵的操作。
有趣的是,您的代码CoffeeService是以完全可测试的方式编写的:只需使用Mockito之类的库来模拟存储库类,然后就可以测试服务逻辑而完全没有任何弹性。您将不需要任何弹簧滑块,任何弹簧注释。您还将看到这些测试的运行速度更快。
class MyServiceTest {
@Test
public void test_my_service_get_coffee_logic() {
// setup
CoffeeRepository repo = Mockito.mock(CoffeeRepository.class);
Mockito.when(repo.findByName("mocha")).thenReturn("coffeeFound");
CoffeeService underTest = new CoffeeService(repo);
// when:
String actualCoffee = underTest.getCoffee("mocha");
// then:
assertEquals(actualCoffee, "coffeeFound");
}
}
Run Code Online (Sandbox Code Playgroud)
现在关于弹簧测试库
您可以将其视为测试代码的一种方法,该代码需要与其他组件进行某些互连,并且很难模拟所有内容。它是同一JVM内的一种集成测试。您提供的所有方式都在运行应用程序上下文,这实际上是一个非常复杂的事情,在youtube上有很多关于应用程序上下文启动期间实际发生情况的会议-尽管超出了问题的范围,关键是执行上下文启动需要时间
@SpringBootTest 更进一步,尝试模仿由Spring Boot框架添加的用于创建上下文的过程:根据包结构决定要扫描的内容,从预定义的位置加载外部配置(可选),运行自动配置启动器,依此类推。
现在,可能会加载应用程序中所有bean的应用程序上下文可能非常大,对于某些测试,它不是必需的。它通常取决于测试的目的
例如,如果您测试rest控制器(正确放置了所有注释),则可能不需要启动数据库连接。
您提供的所有方式都会过滤应该运行的内容,要加载和注入的bean。
通常,这些限制适用于“层”,而不适用于单个bean(层=其余层,数据层等)。
第二和第三种方法实际上是相同的,它们是“过滤”应用程序上下文的方法,仅保留必要的bean。
更新:
由于您已经完成了方法的性能比较:
单元测试=非常快的测试,其目的是验证您编写的代码(或您的同事之一),因此,如果您运行Spring,它的自动运行意味着相对较慢的测试。所以回答你的问题
使用@ContextConfiguration是否可以作为“单元测试”
不,它不能,它是一个集成测试,在春季仅运行一个类。
通常,我们不会只使用Spring Framework运行一个类。如果只想测试一个类(一个单元)的代码,在spring容器中运行它有什么好处?是的,在某些情况下,它可以是几个类,但不能是几十个或几百个。
如果您使用spring运行一个类,那么在任何情况下,您都必须模拟其所有依赖项,可以通过模仿来完成...
现在关于您的问题
@ContextConfiguration与@SpringBootTest的技术差异。
@SpringBootTest仅当您具有Spring Boot应用程序时才有意义。该框架在幕后使用Spring,但总的来说,它附带了许多预定义的配方/实践,以编写应用程序的“基础结构”:-配置管理,-包结构,-可插拔性-日志记录-数据库集成等。
因此,Spring Boot建立了定义明确的流程来处理所有上述项目,如果您想启动模仿Spring Boot应用程序的测试,则可以使用@SpringBootTest批注。否则(或者如果您只有弹簧驱动的应用程序而没有弹簧靴)-完全不要使用它。
@ContextConfiguration虽然是完全不同的事情。它只是说您想在Spring驱动的应用程序中使用什么bean(它也可以在spring boot中使用)
“单元测试”是使用@ContextConfiguration的正确方法吗?或不?
就像我说的那样-所有与弹簧测试有关的东西都仅用于集成测试,所以不,这是在单元测试中使用的错误方法。对于单元测试,应该使用完全不使用spring的东西(例如mockito用于模拟,而常规的junit测试则不使用springRunner)。
小智 6
就像 @MarkBramnik 所说,如果您打算编写单元测试,则必须模拟使用您正在测试的特定组件的其他组件。
@SpringBootTest如果您想编写模拟应用程序流程的集成测试,建议使用。
当您单元测试中的组件并且必须设置该类或创建 bean 的类的配置@ContextConfiguration时使用。@Autowired
| 归档时间: |
|
| 查看次数: |
1519 次 |
| 最近记录: |