iis服务器助手广告广告
返回顶部
首页 > 资讯 > 后端开发 > Python >使用Spring Boot进行单元测试详情
  • 511
分享到

使用Spring Boot进行单元测试详情

2024-04-02 19:04:59 511人浏览 八月长安

Python 官方文档:入门教程 => 点击学习

摘要

目录前言使用 Spring Boot 进行测试系列文章依赖项不要在单元测试中使用spring创建一个可测试的类实例属性注入是不好的提供一个构造函数减少模板代码使用Mockito来模拟

前言

本文给你提供在Spring Boot 应用程序中编写好的单元测试的机制,并且深入技术细节。

我们将带你学习如何以可测试的方式创建Spring Bean实例,然后讨论如何使用MockitoAssertJ,这两个包在Spring Boot中都为了测试默认引用了。

本文只讨论单元测试。至于集成测试,测试WEB层和测试持久层将会在接下来的系列文章中进行讨论。

使用 Spring Boot 进行测试系列文章

这个教程是一个系列:

  • 使用 Spring Boot 进行单元测试(本文)
  • 使用 Spring Boot 和 @WebmvcTest 测试springMVC controller层
  • 使用 Spring Boot 和 @DataJpaTest 测试JPA持久层查询
  • 通过 @SpringBootTest 进行集成测试

如果你喜欢看视频教程,可以看看Philip的课程:测试Spring Boot应用程序课程

依赖项

本文中,为了进行单元测试,我们会使用JUnit Jupiter(Junit 5)MockitoAssertJ。此外,我们会引用Lombok来减少一些模板代码:

dependencies{
  compileOnly('org.projectlombok:lombok')
  testCompile('org.springframework.boot:spring-boot-starter-test')
  testCompile 'org.junit.jupiter:junit-jupiter-engine:5.2.0'
  testCompile('org.mockito:mockito-junit-jupiter:2.23.0')
}

MockitoAssertJ会在spring-boot-test依赖中自动引用,但是我们需要自己引用Lombok

不要在单元测试中使用Spring

如果你以前使用Spring或者Spring Boot写过单元测试,你可能会说我们不要在写单元测试的时候用Spring。但是为什么呢?

考虑下面的单元测试类,这个类测试了ReGISterUseCase类的单个方法:

@ExtendWith(SpringExtension.class)
@SpringBootTest
class RegisterUseCaseTest {

  @Autowired
  private RegisterUseCase registerUseCase;

  @Test
  void savedUserHasRegistrationDate() {
    User user = new User("zaphod", "zaphod@mail.com");
    User savedUser = registerUseCase.registerUser(user);
    assertThat(savedUser.getRegistrationDate()).isNotNull();
  }

}

这个测试类在我的电脑上需要大概4.5秒来执行一个空的Spring项目

但是一个好的单元测试仅仅需要几毫秒。否则就会阻碍TDD(测试驱动开发)流程,这个流程倡导“测试/开发/测试”。

但是就算我们不使用TDD,等待一个单元测试太久也会破坏我们的注意力。

执行上述的测试方法事实上仅需要几毫秒。剩下的4.5秒是因为@SpringBootTest告诉了 Spring Boot 要启动整个Spring Boot 应用程序上下文。

所以我们启动整个应用程序仅仅是因为要把RegisterUseCase实例注入到我们的测试类中。启动整个应用程序可能耗时更久,假设应用程序更大、Spring需要加载更多的实例到应用程序上下文中。

所以,这就是为什么不要在单元测试中使用Spring。坦白说,大部分编写单元测试的教程都没有使用Spring Boot

创建一个可测试的类实例

然后,为了让Spring实例有更好的测试性,有几件事是我们可以做的。

属性注入是不好的

让我们以一个反例开始。考虑下述类:

@Service
public class RegisterUseCase {

  @Autowired
  private UserRepository userRepository;

  public User registerUser(User user) {
    return userRepository.save(user);
  }

}

这个类如果没有Spring没法进行单元测试,因为它没有提供方法传递UserRepository实例。因此我们只能用文章之前讨论的方式-让Spring创建UserRepository实例,并通过@Autowired注解注入进去。

这里的教训是:不要用属性注入。

提供一个构造函数

实际上,我们根本不需要使用@Autowired注解:

@Service
public class RegisterUseCase {

  private final UserRepository userRepository;

  public RegisterUseCase(UserRepository userRepository) {
    this.userRepository = userRepository;
  }

  public User registerUser(User user) {
    return userRepository.save(user);
  }

}

这个版本通过提供一个允许传入UserRepository实例参数的构造函数来允许构造函数注入。在这个单元测试中,我们现在可以创建这样一个实例(或者我们之后要讨论的Mock实例)并通过构造函数注入了。

当创建生成应用上下文的时候,Spring会自动使用这个构造函数来初始化RegisterUseCase对象。注意,在Spring 5 之前,我们需要在构造函数上增加@Autowired注解,以便让Spring找到这个构造函数。

还要注意的是,现在UserRepository属性是final修饰的。这很重要,因为这样的话,应用程序生命周期时间内这个属性内容不会再变化。此外,它还可以帮我们避免变成错误,因为如果我们忘记初始化该属性的话,编译器就报错。

减少模板代码

通过使用Lombok@RequiredArgsConstructor注解,我们可以让构造函数自动生成:

@Service
@RequiredArgsConstructor
public class RegisterUseCase {

  private final UserRepository userRepository;

  public User registerUser(User user) {
    user.setRegistrationDate(LocalDateTime.now());
    return userRepository.save(user);
  }

}

现在,我们有一个非常简洁的类,没有样板代码,可以在普通的 java 测试用例中很容易被实例化:

class RegisterUseCaseTest {

  private UserRepository userRepository = ...;

  private RegisterUseCase registerUseCase;

  @BeforeEach
  void initUseCase() {
    registerUseCase = new RegisterUseCase(userRepository);
  }

  @Test
  void savedUserHasRegistrationDate() {
    User user = new User("zaphod", "zaphod@mail.com");
    User savedUser = registerUseCase.registerUser(user);
    assertThat(savedUser.getRegistrationDate()).isNotNull();
  }

}

还有部分确实,就是如何模拟测试类所依赖的UserReposity实例,我们不想依赖真实的类,因为这个类需要一个数据库连接。

使用Mockito来模拟依赖项

现在事实上的标准模拟库是 Mockito。它提供至少两种方式来创建一个模拟UserRepository实例,来填补前述代码的空白。

使用普通Mockito来模拟依赖

第一种方式是使用Mockito编程:

private UserRepository userRepository = Mockito.mock(UserRepository.class);

这会从外界创建一个看起来像UserRepository的对象。默认情况下,方法被调用时不会做任何事情,如果方法有返回值,会返回null

因为userRepository.save(user)返回null,现在我们的测试代码assertThat(savedUser.getRegistrationDate()).isNotNull()会报空指针异常(NullPointerException)。

所以我们需要告诉Mockito,当userRepository.save(user)调用的时候返回一些东西。我们可以用静态的when方法实现:

@Test
void savedUserHasRegistrationDate() {
  User user = new User("zaphod", "zaphod@mail.com");
  when(userRepository.save(any(User.class))).then(returnsFirstArg());
  User savedUser = registerUseCase.registerUser(user);
  assertThat(savedUser.getRegistrationDate()).isNotNull();
}

这会让userRepository.save()返回和传入对象相同的对象。

Mockito为了模拟对象、匹配参数以及验证方法调用,提供了非常多的特性。想看更多,文档

通过Mockito@Mock注解模拟对象

创建一个模拟对象的第二种方式是使用Mockito@Mock注解结合 JUnit Jupiter的MockitoExtension一起使用:

@ExtendWith(MockitoExtension.class)
class RegisterUseCaseTest {

  @Mock
  private UserRepository userRepository;

  private RegisterUseCase registerUseCase;

  @BeforeEach
  void initUseCase() {
    registerUseCase = new RegisterUseCase(userRepository);
  }

  @Test
  void savedUserHasRegistrationDate() {
    // ...
  }

}

@Mock注解指明那些属性需要Mockito注入模拟对象。由于JUnit不会自动实现,MockitoExtension则告诉Mockito来评估这些@Mock注解。

这个结果和调用Mockito.mock()方法一样,凭个人品味选择即可。但是请注意,通过使用 MockitoExtension,我们的测试用例被绑定到测试框架

我们可以在RegisterUseCase属性上使用@InjectMocks注解来注入实例,而不是手动通过构造函数构造。Mockito会使用特定的算法来帮助我们创建相应实例对象:

@ExtendWith(MockitoExtension.class)
class RegisterUseCaseTest {

  @Mock
  private UserRepository userRepository;

  @InjectMocks
  private RegisterUseCase registerUseCase;

  @Test
  void savedUserHasRegistrationDate() {
    // ...
  }

}

使用AssertJ创建可读断言

Spring Boot 测试包自动附带的另一个库是AssertJ。我们在上面的代码中已经用到它进行断言:

assertThat(savedUser.getRegistrationDate()).isNotNull();

然而,有没有可能让断言可读性更强呢?像这样,例子:

assertThat(savedUser).hasRegistrationDate();

有很多测试用例,只需要像这样进行很小的改动就能大大提高可理解性。所以,让我们在test/sources中创建我们自定义的断言吧:

class UserAssert extends AbstractAssert<UserAssert, User> {

  UserAssert(User user) {
    super(user, UserAssert.class);
  }

  static UserAssert assertThat(User actual) {
    return new UserAssert(actual);
  }

  UserAssert hasRegistrationDate() {
    isNotNull();
    if (actual.getRegistrationDate() == null) {
      failWithMessage(
        "Expected user to have a registration date, but it was null"
      );
    }
    return this;
  }
}

现在,如果我们不是从AssertJ库直接导入,而是从我们自定义断言类UserAssert引入assertThat方法的话,我们就可以使用新的、更可读的断言。

创建一个这样自定义的断言类看起来很费时间,但是其实几分钟就完成了。我相信,将这些时间投入到创建可读性强的测试代码中是值得的,即使之后它的可读性只有一点点提高。我们编写测试代码就一次,但是之后,很多其他人(包括未来的我)在软件生命周期中,需要阅读、理解然后操作这些代码很多次。

如果你还是觉得很费事,可以看看断言生成器

结论

尽管在测试中启动Spring应用程序也有些理由,但是对于一般的单元测试,它不必要。有时甚至有害,因为更长的周转时间。换言之,我们应该使用更容易支持编写普通单元测试的方式构建Spring实例。

Spring Boot Test Starter附带MockitoAssertJ作为测试库。让我们利用这些测试库来创建富有表现力的单元测试!

到此这篇关于使用Spring Boot进行单元测试详情的文章就介绍到这了,更多相关Spring Boot单元测试内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: 使用Spring Boot进行单元测试详情

本文链接: https://www.lsjlt.com/news/168213.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • 使用Spring Boot进行单元测试详情
    目录前言使用 Spring Boot 进行测试系列文章依赖项不要在单元测试中使用Spring创建一个可测试的类实例属性注入是不好的提供一个构造函数减少模板代码使用Mockito来模拟...
    99+
    2022-11-13
  • 如何Spring Boot中使用MockMvc对象进行单元测试
    这期内容当中小编将会给大家带来有关如何Spring Boot中使用MockMvc对象进行单元测试,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。Spring测试框架提供MockMvc对象,可以在不需要客户端...
    99+
    2023-05-31
    springboot mockmvc
  • 如何使用Spring-Test对Spring框架进行单元测试
    目录Spring-Test对Spring框架进行单元测试加载依赖编写SpringTestBase基础类,加载所需xml文件编写单元测试类 示例Spring-Test测试数据1、新建一...
    99+
    2022-11-12
  • C#使用MSTest进行单元测试
    我之前写过一篇XUNit的简介:使用Xunit来进行单元测试。Xunit在当时确实是一个最简单易用的测试框架,然而,随着发展,Xunit也变得复杂了不少,光写一个最简单的测试就要导入...
    99+
    2022-11-13
  • Spring Boot中怎么利用JUnit 5实现单元测试
    这篇文章给大家介绍Spring Boot中怎么利用JUnit 5实现单元测试,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。1. 忽略测试用例执行JUnit 4:@Test  @Ignore ...
    99+
    2023-06-16
  • Android中使用Junit进行单元测试
      不管我们在学习还是在开发的时候,都会用到测试,在Android中进行的Junit单元工具测试需要创建一个类去继承于AndroidTestCase类,同时还需要在主配置文...
    99+
    2022-06-06
    单元 junit 单元测试 测试 Android
  • Spring MVC中的Controller进行单元测试的实现
    目录导入静态工具方法初始化MockMvc执行测试测试GET接口测试POST接口测试文件上传定义预期结果写在最后对Controller进行单元测试是Spring框架原生就支持的能力,它...
    99+
    2022-11-13
  • .Net使用Xunit工具进行单元测试
    不管你爱与不爱,单元测试对于一个软件的长治久安还是必不可少的一环。在Visual Studio 2012后,VS中的测试浏览器也能与第三方的集成了,用起来还是非常方便的。目前在.Ne...
    99+
    2022-11-13
  • C#怎么使用MSTest进行单元测试
    今天小编给大家分享一下C#怎么使用MSTest进行单元测试的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。目前MS发布了两个版...
    99+
    2023-07-02
  • 【星云测试】开发者测试-采用精准测试工具对Spring Boot应用进行测试
    简介:本文主要介绍把现今主流的springboot框架项目和精准测试工具进行结合和应用,通过精准测试的数据穿透、数据采集、测试用例与代码的双向追溯、数据分析等一系列精准测试的特有功能,达到对项目质量的保证。 本次环境搭建分为基础环...
    99+
    2023-06-02
  • 使用Junit对Android应用进行单元测试
      在本文中,你将会学习到如何在Eclipse中创建Android JUnit的单元测试工程以及在不同的条件下创建及运行自动测试用例。   准备工作   本文假设读...
    99+
    2022-06-06
    单元 junit 单元测试 测试 Android
  • C++中怎么使用CppUnit进行单元测试
    这篇文章主要讲解了“C++中怎么使用CppUnit进行单元测试”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“C++中怎么使用CppUnit进行单元测试”吧!如果使用VC6,那么直接用VC6打...
    99+
    2023-06-17
  • ASP.NET Core项目使用xUnit进行单元测试
    目录一、前言二、创建示例项目1、UnitTest.Model2、UnitTest.Data3、UnitTest.IRepository4、UnitTest.Repository5、U...
    99+
    2022-11-13
  • Android中如何使用JUnit进行单元测试
      在我们日常开发android app的时候,需要不断地进行测试,所以使用JUnit测试框架显得格外重要,学会JUnit可以加快应用的开发周期。   Android中建...
    99+
    2022-06-06
    单元 junit 单元测试 测试 Android
  • 如何使用MockMvc进行controller层单元测试
    这篇文章主要介绍了如何使用MockMvc进行controller层单元测试,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。看代码吧~package com.ieou....
    99+
    2023-06-15
  • C#中如何使用单元测试框架进行自动化测试
    C#中如何使用单元测试框架进行自动化测试引言:在软件开发过程中,自动化测试是一个非常重要的环节。通过编写和运行测试代码,可以帮助我们验证和确保代码的正确性和稳定性。在C#开发中,我们可以使用单元测试框架来实现自动化测试。本文将介绍C#中常用...
    99+
    2023-10-22
    自动化测试 C# 单元测试
  • Java单元测试Mockito的使用详解
    Mockito简介     调用mock对象的方法时,不会执行真实的方法,而是返回类型的默认值,如object返回null, int返回0等,否则通过...
    99+
    2022-11-12
  • Java使用Junit4.jar进行单元测试的方法
    目录一、下载依赖包二、添加到依赖三、设置 test 目录四、创建测试类五、开始测试一、下载依赖包 分别下载 junit.jar 以及 hamcrest-core.jar 二、添加到依...
    99+
    2022-11-12
  • .Net怎么使用Xunit工具进行单元测试
    本篇内容介绍了“.Net怎么使用Xunit工具进行单元测试”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!安装Xunit:Xunit的安装现在...
    99+
    2023-07-02
  • 如何使用Spring AOP进行测试
    本篇内容主要讲解“如何使用Spring AOP进行测试”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“如何使用Spring AOP进行测试”吧!AOP解析今天来介...
    99+
    2022-10-19
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作