很难为干净的代码找到一个好的定义,因为我们每个人都有自己的单词clean的定义。 但是,有一个似乎是通用的定义:

干净的代码易于阅读。

这可能会让您感到有些惊讶,但是我认为该定义也适用于测试代码。 使测试尽可能具有可读性是我们的最大利益,因为:

  • 如果我们的测试易于阅读,那么很容易理解我们的代码是如何工作的。
  • 如果我们的测试易于阅读,那么如果测试失败(不使用调试器),很容易发现问题。

编写干净的测试并不难,但是需要大量的实践,这就是为什么如此多的开发人员为此苦苦挣扎的原因。

我也为此感到挣扎,这就是为什么我决定与您分享我的发现的原因。

这是我的教程的第一部分,描述了我们如何编写干净的测试。 这次,我们将学习如何以简单干净的方式配置测试用例。

问题

假设我们必须使用Spring MVC Test框架为Spring MVC控制器编写“单元测试”。 我们要测试的第一个控制器称为TodoController ,但是我们还必须为应用程序的其他控制器编写“单元测试”。

作为开发人员,我们知道重复的代码是一件坏事。 在编写代码时,我们遵循“ 不要重复自己(DRY)”原则 ,该原则指出:

每条知识都必须在系统中具有单一,明确,权威的表示形式。

我怀疑这是开发人员经常在其测试套件中使用继承的原因之一。 他们将继承视为重用代码和配置的廉价且简便的方法。 这就是为什么他们将所有通用代码和配置放入实际测试类的一个或多个基类。

让我们看看如何使用该方法配置“单元测试”。

首先 ,我们必须创建一个抽象基类, 该基类可以配置Spring MVC Test框架,并通过实现setUpTest(MockMvc mockMvc)方法来确保其子类可以提供其他配置。

AbstractControllerTest类的源代码如下所示:

import org.junit.Before;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {WebUnitTestContext.class})
@WebAppConfiguration
public abstract class AbstractControllerTest {private MockMvc mockMvc;@Autowiredprivate WebApplicationContext webAppContext;@Beforepublic void setUp() {mockMvc = MockMvcBuilders.webAppContextSetup(webAppContext).build();setupTest(MockMvc mockMvc)}protected abstract void setUpTest(MockMvc mockMvc);
}

其次 ,我们必须实现实际的测试类,该类创建所需的模拟和新的控制器对象。 TodoControllerTest类的源代码如下所示:

import org.mockito.Mockito;
import org.springframework.test.web.servlet.MockMvc;public class TodoControllerTest extends AbstractControllerTest {private MockMvc mockMvc;@Autowiredprivate TodoService serviceMock;@Overrideprotected void setUpTest(MockMvc mockMvc) {Mockito.reset(serviceMock);this.mockMvc = mockMvc;}//Add test methods here
}

这个测试类看起来很干净,但是有一个主要缺陷:

如果我们想了解测试用例的配置方式,则必须阅读TodoControllerTestAbstractControllerTest类的源代码。

这似乎是一个小问题,但这意味着我们必须将注意力从测试用例转移到基类(或多个类)上。 这需要精神上的上下文切换,并且上下文切换非常昂贵

您可能当然会争辩说,在这种情况下使用继承的精神代价非常低,因为配置非常简单。 的确如此,但是要记住,现实生活中的情况并非总是如此。

上下文切换的实际成本取决于测试类层次结构的深度和配置的复杂性。

解决方案

我们可以通过配置测试类中的所有测试用例来提高配置的可读性。 这意味着我们必须:

  • 将所需的注释(例如@RunWith )添加到测试类。
  • 将设置和拆卸方法添加到测试类。

如果我们遵循以下规则修改示例测试类,则其源代码如下:

import org.junit.Before;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {WebUnitTestContext.class})
@WebAppConfiguration
public class TodoControllerTest {private MockMvc mockMvc;@Autowiredprivate TodoService serviceMock;@Autowiredprivate WebApplicationContext webAppContext;@Beforepublic void setUp() {Mockito.reset(serviceMock);mockMvc = MockMvcBuilders.webAppContextSetup(webAppContext).build();}//Add test methods here
}

在我看来,我们的测试用例的新配置比旧的配置(分为TodoControllerTestAbstractControllerTest类)看起来更加简单和简洁。

不幸的是,没有什么是免费的。

这是一个权衡

每个软件设计决策都需要权衡利弊。 这不是该规则的例外

在测试类中配置我们的测试用例具有以下好处:

  1. 我们无需阅读测试类的所有超类即可了解测试用例的配置。 这样可以节省大量时间,因为我们不必将注意力从一堂课转移到另一堂课。 换句话说, 我们不必付出上下文切换的代价
  2. 当测试失败时,可以节省时间。 如果为了避免重复的代码或配置而使用继承,则很可能我们的基类将包含与某些但不是全部测试用例相关的组件。 换句话说,我们将确定哪些组件与失败的测试用例相关,这可能不是一件容易的事。 在测试类中配置测试用例时, 我们知道每个组件都与失败的测试用例有关

另一方面,这种方法的缺点是:

  1. 我们必须编写重复的代码。 这比将所需的配置放置到一个或多个基类上花费的时间更长。
  2. 如果任何使用的库以迫使我们修改测试配置的方式进行更改,则我们必须对每个测试类进行必要的更改。 这显然比仅对基类(或多个基类)进行这些操作要慢得多。

如果我们唯一的目标是尽可能快地编写测试,那么很明显,我们应该消除重复的代码和配置。

但是,这不是我唯一的目标。

我认为这种方法的优点胜于缺点的原因有两个:

  1. 继承不是重用代码或配置的正确工具 。
  2. 如果测试用例失败,我们必须尽快找到并解决问题,并且干净的配置将帮助我们实现该目标。

我在这件事上的立场是明确的。 但是,仍然存在一个非常重要的问题:

您会做出其他折衷吗?

翻译自: https://www.javacodegeeks.com/2014/05/writing-clean-tests-it-starts-from-the-configuration.html

编写干净的测试–从配置开始相关推荐

  1. 怎样编写测试类测试分支_编写干净的测试–从配置开始

    怎样编写测试类测试分支 很难为干净的代码找到一个好的定义,因为我们每个人都有自己的单词clean的定义. 但是,有一个似乎是通用的定义: 简洁的代码易于阅读. 这可能会让您感到有些惊讶,但我认为该定义 ...

  2. 怎样编写测试类测试分支_编写干净的测试–天堂中的麻烦

    怎样编写测试类测试分支 如果我们的代码有明显的错误,我们很有动力对其进行改进. 但是,在某些时候,我们认为我们的代码"足够好"并继续前进. 通常,当我们认为改进现有代码的好处小于所 ...

  3. 怎样编写测试类测试分支_编写干净的测试-被认为有害的新内容

    怎样编写测试类测试分支 很难为干净的代码找到一个好的定义,因为我们每个人都有自己的单词clean的定义. 但是,有一个似乎是通用的定义: 简洁的代码易于阅读. 这可能会让您感到有些惊讶,但我认为该定义 ...

  4. c++返回指针时候注意提防_编写干净的测试–提防魔术

    c++返回指针时候注意提防 很难为干净的代码找到一个好的定义,因为我们每个人都有自己的单词clean的定义. 但是,有一个似乎是通用的定义: 简洁的代码易于阅读. 这可能会让您感到有些惊讶,但我认为该 ...

  5. 编写干净的测试–天堂中的麻烦

    如果我们的代码有明显的错误,我们很有动力进行改进. 但是,在某些时候,我们认为我们的代码"足够好"并继续前进. 通常,当我们认为改进现有代码的好处小于所需的工作时,就会发生这种情况 ...

  6. 编写干净的测试-被认为有害的新内容

    很难为干净的代码找到一个好的定义,因为我们每个人都有自己的单词clean的定义. 但是,有一个似乎是通用的定义: 干净的代码易于阅读. 这可能会让您感到有些惊讶,但是我认为该定义也适用于测试代码. 使 ...

  7. 编写干净的测试–提防魔术

    很难为干净的代码找到一个好的定义,因为我们每个人都有自己的单词clean的定义. 但是,有一个似乎是通用的定义: 干净的代码易于阅读. 这可能会让您感到有些惊讶,但是我认为该定义也适用于测试代码. 使 ...

  8. 断言工具的编写_编写干净的测试–用特定领域的语言替换断言

    断言工具的编写 很难为干净的代码找到一个好的定义,因为我们每个人都有自己的单词clean的定义. 但是,有一个似乎是通用的定义: 简洁的代码易于阅读. 这可能会让您感到有些惊讶,但我认为该定义也适用于 ...

  9. 分而治之_编写干净的测试–分而治之

    分而治之 好的单元测试应该仅出于一个原因而失败. 这意味着适当的单元测试仅测试一个逻辑概念. 如果我们要编写干净的测试,则必须识别那些逻辑概念,并且每个逻辑概念只编写一个测试用例. 这篇博客文章描述了 ...

最新文章

  1. MyBatis常规CURD详解及拓展~
  2. 知名网站的 404 页面长啥样?
  3. 抢人饭碗了!推荐一款全自动的机器学习建模神器PyCaret
  4. ASP.NET Core MVC 之依赖注入 View
  5. 反射 getDeclaredMethod和getMethod的区别以及用法《实例》
  6. 洛谷 2633 BZOJ 2588 Spoj 10628. Count on a tree
  7. 心法利器[57] | 文本多分类问题经验
  8. 【电路】PT1000/PT100温度采集电路
  9. 1013: 【顺序结构】数字各位分割
  10. 官方固件库V1.4版本介绍
  11. Javascript基础之-强制类型转换(三) 1
  12. C#实现驱动级模拟按键
  13. RLS算法到卡尔曼滤波 II
  14. mysql_assoc函数_关于PHP的函数mysql_fetch_assoc的问题
  15. 一款最好用的windows文件管理器
  16. 权利要求的撰写 分享
  17. php数组合并多维,PHP合并多维数组
  18. 《金字塔原理》读书思维导图
  19. 加拿大11年级计算机课程代码,加拿大读11年级,我经历的选课、学英语、拿高分...
  20. 最新,有8本SCIE期刊被剔除,4月SCIESSCI期刊目录更新(附最新目录下载)

热门文章

  1. 生成方法中参数的注释
  2. mmall工程生成dao层的插件出错了出错了 自动生成mapper 的插件出错了
  3. 贪婪算法+小应用(调度问题)
  4. 在diy的文件系统上创建文件的流程
  5. //rest风格写_REST /使用提要发布事件
  6. java 正则表达式 开头_如何在Java中修复表达式的非法开头
  7. java agent_如何脚踏实地构建Java Agent
  8. Hibernate架构概述
  9. kafka数据到flume_大数据摄取:Flume,Kafka和NiFi
  10. Infinispan版本已映射到最低Java版本