fxleyu / west-world

This is a repository for the records of books, films, teleplay and so on.
https://fxleyu.github.io/
0 stars 0 forks source link

[阅读笔记][BS] 第 7 章 使用 Spring 进行测试驱动开发 #20

Open fxleyu opened 6 years ago

fxleyu commented 6 years ago

来自于《Spring 入门经典》#4 的阅读笔记。

本章主要内容:

如果在一个应用程序中应用了控制反转(IoC)模型,则可以使代码库更容易进行单元测试。也可以非常容易地创建 Mock 依赖项并将它们注入到需要进行测试的对象中。针对单元测试,需要重点关注的中心单元是测试中的类或者方法。在单元测试期间,不应该有任何环境依赖,比如数据库、网络甚至是 IoC 容器。然而,分开进行单元测试是不够的。

需要将整个系统的一部分组合起来,以便查看它们是否工作正常。在一个软件系统中,这些组合起来的部分通常包含来自不同层的对象,比如现在的事务上下文和数据库、网络交互、安全上下文或者 IoC 容器。如果可以在不必将整个应用程序部署到应用服务器上并运行的前提下进行集成测试,那么就非常好了。

Spring 提供了一流的集成测试支持来帮组开发人员编写集成测试代码,并且不必部署和运行整个系统。所谓的 Spring TextContext Framework 完全独立于实际的测试框架,如果需要在一个独立的环境中进行,可以使用该框架。

Spring TextContext Framework 的主要目标是简化 Spring 容器的配置和创建,并将依赖项注入到 Bean 以及测试套件中。可以在现有的事务上下文中测试数据库交互以及对象-关系影响(ORM)代码,以便开发人员可以确保正确地完成了 ORM 映射,查询有效并返回了预期的结果,等等。此外,还可以非常容易地测试 Web 功能,并且不必将应用程序部署到 Web 容器中。

本章将重点介绍 Spring Application Framework 中可以帮助我们在一个独立的环境中测试代码的相关功能。

fxleyu commented 6 years ago

本章主要知识点

主题 关键知识点
@RunWith 用来指定一个自定义 JUnit Runner 类的 JUnit 注解,该类将用来执行测试类。
SpringJUnit4ClassRunner 用来执行测试的 Spring TestContext Framework 的 JUnit Runner 实现。
@ContextConfiguration 用来指定配置元数据的注解,通过这些元数据为测试夹具创建 ApplicationContext
@ActiveProfiles 当创建 ApplicationContext 时,可以使用该注解指定哪些配置文件值被激活
@DirtiesContext 通过该注解告诉 TestContext Framework,在执行下一个测试之前应该抛弃 ApplicationContext
@Autowired@Inject@Resource 用来将测试夹具注入到测试类中的依赖注入注解
ApplicationContextInitializer 在初始化期间用来配置 ApplicationContext 的 API
@TransactionConfiguration 对框架的事务管理配置进行配置和自定义的注解
@Rollback 在测试方法结束后用来控制当前事务划分结果的注解
@BeforeTransaction@AfterTransaction 分别为安装和拆卸注解,表示此类代码应该在当前事务划分之前执行
@WebAppConfiguration 用来在一个单独的测试环境中启动 WebApplicationContext 的注解
@ContextHierarchy 用来在测试环境中创建父-子 ApplicationContext 层次结构的注解
MockHttpServletRequest、MockHttpServletResponse、MockHttpSession、MockServletContext、MockServletConfig Spring TestContext Framework 所提供的用来帮助测试 Web 功能的 Servlet API Mock 对象。
@IfProfileValue 用来有条件地运行测试的注解
@Timed 用来在制定的时间限制内运行测试的注解
@Repead 用来连续多次运行测试的注解
MockMvc 测试中所使用的主要实现类。它通过一个 WebApplicationContext 构建,并执行模拟的 HTTP 请求操作
fxleyu commented 6 years ago

1 配置和缓存 ApplicationContext

当运行测试代码时,Spring 能够自动创建和管理 ApplicationContex。可用使用配置元数据的不同格式:基于 XML,基于注解或者基于 Java。还可以在任何应用程序中使用多种配置元数据格式。Spring TextContext Framework 支持在进行集成测试时所有的不同格式的配置元数据。

1.1 在测试中使用基于 XML 和基于 Java 的上下文配置

在测试配置中使用基于 XML 的配置元数据:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:spring-annotation.xml")
public class AopTest {
    @Autowired
    private MyBean myBean;

    @Test(expected = IllegalArgumentException.class)
    public void testMyBean() throws InterruptedException {
        myBean.sayHello("world");
    }
}

在测试配置中使用基于 Java 的配置元数据:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = ApplicationConfig.class)
public class AopTest {
    @Autowired
    private MyBean myBean;

    @Test(expected = IllegalArgumentException.class)
    public void testMyBean() throws InterruptedException {
        myBean.sayHello("world");
    }
}

1.2 使用 ApplicationContextInitializer 配置上下文

fxleyu commented 6 years ago

2 注入测试夹具的依赖项

Spring TestContext Framework 可用将从配置的 ApplicationContext 中解析的 Bean 注入到测试实例的属性中,可用使用 @Resource@Inject 以及 @Autowired 注解来表示哪些属性可用作为一个依赖而注入。

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = ApplicationConfig.class)
public class AopTest {
    @Autowired
    private MyBean myBean;

    @Test(expected = IllegalArgumentException.class)
    public void testMyBean() throws InterruptedException {
        myBean.sayHello("world");
    }
}

在默认情况下,@Autowired 根据类型执行依赖注入。

@Inject@Named 注解是在 Java 规范的 JSR-330 Dependency Injection 中被引入。它们分别对应 @Autowired@Qualifier 注解。

fxleyu commented 6 years ago

3 在测试中使用事务管理

Spring TestContext Framework 支持在一个活动事务上下文中执行测试。

fxleyu commented 6 years ago

4 测试 Web 应用程序

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = AppConfig.class)
@WebAppConfiguration
public class ApplicationTest {
    @Autowired
    private WebApplicationContext applicationContext;
    @Autowired
    private MockServletContext servletContext;

    @Test
    public void testApplication() {
        Assert.assertNotNull(applicationContext);

        Assert.assertNotNull(servletContext);
    }
}

4.1 测试中的上下文层次结构

fxleyu commented 6 years ago

5 使用 Mock 对象以及其他用于测试的实用工具

一般来说,有用的对象不会单独存在,并且需要使用环境中的其他对象才可用进行操作。在应用程序中,它们彼此之间相互协作,形成了一个更为复杂的对象网络。这些被其他对象用来完成预期行为的对象被称为依赖项。在操作执行期间,一个对象可能需要多个依赖项。一些依赖对象可能与数据库相交互;一些可能与文件系统相交互或者涉及网络通信。

另一方面,单元测试又需要专注于对单个对象的测试。被测试的操作应该可用在没有任何其他依赖的情况下工作。如果只需向被测试的方法提供所需的输入值,然后调用该方法,最后根据所期望的结果检查返回值(如果存在的话),那么对于这种测试方案,单元测试是不会出现任何问题的。如果被测试的对象与其他依赖对象相绑定,那么这些依赖项也需要与自己的依赖项相绑定,依此类推。此外,被测试的操作可能需要通过其他依赖对象连接数据库或者建立一个网络连接来完成请求。

因此,单元测试将被测试的对象与其依赖项绑定起来,以便该对象可用操作而不出现任何问题。但这些被注入到测试对象中的依赖项只是一个替代品。它们并不会实际连接数据库或建立一个网络连接,虽然从表面上看它们似乎是这样做的。此类对象被称为 Mock 对象。Mock 对象可用根据测试方案来调整自己的行为。因此,当被测试的对象与 Mock 对象交互时,Mock 对象仅返回预期的值,以便被测试的对象可用完成针对特定方案的操作。在测试方法完成之后,还需要查询 Mock 对象的状态,以便检查被测试对象是否按预期与 Mock 对象进行了交互。

fxleyu commented 6 years ago

6 小结

在本章,我们学习了 Spring 针对应用程序所提供的一流的集成测试支持。可以非常容易地为测试类创建和管理 ApplicationContext。依赖注入由 TestContext Framework 自动完成。处于性能考虑,ApplicationContext 被缓存并且在多个测试类之间重复使用。测试与数据库和持久化相关的代码一直依赖都存在的问题,因为数据更改可能会对其他测试产生副作用。然而,Spring 为测试提供了自动事务划分功能,从而很好地解决了这个副作用问题,其解决的思路是:在测试方案结束时进行事务回滚,当测试执行完毕后,就不会保留对数据的更改。

本章还讨论了如何根据应用程序的具体需求配置和自定义事务划分功能。此外,还介绍了如何使用 TestContext Framework 在一个独立环境中启动 WebApplicationContext,以及如何通过访问如 HTTP 请求、响应、会话之类的 Mock Web 对象来测试 Web 功能。

最后,本章简要介绍了一下用来测试相关应用程序代码的 Mock 对象,而这些代码通常依赖不同的 API 和 SPI,以及 TestContext Framework 中可用的其他一些实用工具注解。