如果您今天正在编程,那么您很可能听说过单元测试或测试驱动的开发过程。我还没有遇到一个既没有听说过又没有听说过单元测试并不重要的程序员。在随意的讨论中,大多数程序员似乎认为单元测试非常重要。

但是,当我开始使用代码并问“单元测试在哪里?”时,我得到了一个完全不同的故事。我最近在网上问我的程序员朋友为什么不这样做,以及为什么其他程序员不这样做呢?不要编写单元测试。当我问程序员或IT经理同样的问题时,我经常听到的第一答案是:“我没有时间”或类似的问题。通常会出现这样的论点,即使用单元测试编写应用程序要比不使用单元测试编写时间长20%,并且“我们受到时间限制”。

我的建议–当我们尝试解决时间不足的问题时,也许我们可以在娱乐性上做出一些贡献。

在实践中

我正在为一个应用程序设计原型,该应用程序将允许用户输入有关房屋装修项目的信息,然后与朋友共享该项目的材料和工具信息。然后,朋友可以承诺贷款或购买项目中所需的一些材料或工具。基本上是用于家庭装修项目的“登记处”。

测试将在采用Project对象的方法上进行,遍历该项目的工具列表以查看该工具是否已经被承诺,并创建一个未被承诺的工具列表。然后,它将把该列表传递给将查询每个工具当前价格的服务。

原型是用Grails完成的,但是我们将用Java编写此方法:

public List<Tool> neededToolList(Project project) {
final List<Tool> retList = new ArrayList<>(); if (project.getTools() == null || project.getTools().isEmpty()) {
return retList;
} for (Tool tool : project.getTools()) {
if (!tool.getPromise().isPromised()) {
retList.add(tool);
}
} List<Tool> tools = lookupService.updateToolList(retList);
return tools;
}

单个单元测试可能类似于:

@Test
public void testNeededToolList() {
Tools _instance = new Tools(); Project project = new Project(); Promise promise = new Promise();
promise.setProject(project);
promise.setPromised(false);
Promise promise2 = new Promise();
promise2.setProject(project);
promise2.setPromised(true); List<Tool> tools = new ArrayList<>();
List<Tool> lookupTools = new ArrayList<>();
Tool tool1 = new Tool();
tool1.setName("table saw");
tool1.setStoreId("T001");
tool1.setPromise(promise);
tools.add(tool1);
lookupTools.add(tool1);
Tool tool2 = new Tool();
tool2.setName("pneumatic nail guns");
tool2.setStoreId("T027");
tool2.setPromise(promise2);
tools.add(tool2);
project.setTools(tools); List<Tool> mockedTools = new ArrayList<>();
Tool mockedTool1 = new Tool();
mockedTool1.setPromise(promise);
mockedTool1.setName("table saw");
mockedTool1.setStoreId("T001");
mockedTool1.setPrice(129.0);
mockedTools.add(mockedTool1); lookupService = Mockito.mock(LookupServiceImpl.class);
Mockito.when(lookupService.updateToolList(lookupTools)).thenReturn(mockedTools);
_instance.setLookupService(lookupService); List<Tool> returnedTools = _instance.neededToolList(project); assertTrue(returnedTools.size() == 1);
for(Tool tool : returnedTools) {
assertEquals(129.0, tool.getPrice(), 0.01);
}
}

这是一个简单的测试,并且只有一个。需要针对几种情况编写测试,例如空值。例如,如果StoreID不存在怎么办?

输入Groovy

在之前的文章中,我已经介绍了我的好朋友Groovy编程语言。让我们看看是否可以进行Groovy测试。

Groovy带来了许多语法上的捷径,这些捷径有助于加快编写代码(包括测试)的速度。让我们看一下在Groovy中重写该测试的可能方法。

class GroovyToolsTest extends GroovyTestCase {
def lookupService = [
updateToolList : {List<Tool> toolList ->
toolList.each {
if(it.storeId == "T001") {
it.price = 129.0
}
}
return toolList
}
] as LookupService void testNeededToolList() {
def _instance = new Tools()
def project = new Project()
project.tools = [
new Tool(name: "table saw", storeId: "T001", promise: new Promise(project: project, promised: false)),
new Tool(name: "pneumatic nail guns", storeId: "T027", promise: new Promise(project: project, promised: true))
] _instance.lookupService = lookupService def returnedList = _instance.neededToolList(project)
returnedList.size() == 1
returnedList.each {
if(it.storeId == "T001") {
assert it.price == 129.0
}
}
println "done"
}
}

我们看到的第一件事是Groovy为我们提供了一种很棒的Mocking代码机制,它使我们能够做的比我在Mocking框架中所能做的还要多。在模拟框架中,我通常为期望返回的数据创建一个新对象。在这里,我实际上是将数据更改为服务应该返回的内容。

切记:我不是在测试服务,所以模拟服务应该返回我期望服务返回的值。

我还发现可以在一个调用中创建对象并加载数据的功能(与创建Bean和调用每个setter相对)更容易编写,读取和复制为模板,以创建更多内容。Groovy提供了几种处理列表的方法,使之成为快速开发和维护测试的出色语言。

如果您想对单元测试有所不同,那么还有Spock测试框架。它具有更广泛的语言,使其更具行为驱动的外观,但仍使用上一示例中的所有Groovy Goodness。

class ToolsSpec extends Specification {
def lookupService = [
updateToolList : {List<Tool> toolList ->
println "mocked service"
toolList.each { tool ->
if(tool.storeId == "T001")
tool.price = 129.0
}
return toolList
}
] as LookupService def "Lookup needed tool list"() {
given:"Create instance"
def _instance = new Tools()
def project = new Project()
project.tools = [
[name: "table saw", storeId: "T001", promise: [project: project, promised: false] as Promise] as Tool,
[name: "pneumatic nail guns", storeId: "T027", promise: [project: project, promised: true] as Promise] as Tool,
] as List<Tool>; _instance.lookupService = lookupService expect:"Tool List"
def returnedList = _instance.neededToolList(project)
returnedList.size() == 1
returnedList.each {
if(it.storeId == "T001") {
assert it.price == 129.0
}
} } }

请注意,我使用了一种不同的语法为Tool创建测试数据对象。这是标准的Groovy功能,它允许程序员将映射转换为具体的类,并且在先前的示例中也可以使用。当您习惯阅读Groovy时,这可能比新的Object语法更容易阅读。

在这两个示例中,语法“糖”更紧密的代码并不是唯一的好处。测试失败的输出也会有所不同,并且会更有帮助

在第一个示例中,测试失败的输出为:

java.lang.AssertionError: expected:<128.0> but was:<129.0>
at org.junit.Assert.fail(Assert.java:88)
at org.junit.Assert.failNotEquals(Assert.java:834)
at org.junit.Assert.assertEquals(Assert.java:553)
at org.junit.Assert.assertEquals(Assert.java:683)
at org.projectregistry.services.ToolsTest.testNeededToolList(ToolsTest.java:93)
....

Groovy和Spock测试的输出如下所示:

Assertion failed: 

assert it.price == 128.0
| | |
| 129.0 false
org.projectregistry.model.Tool@5e59238b at org.codehaus.groovy.runtime.InvokerHelper.assertFailed(InvokerHelper.java:399)
at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.assertFailed(ScriptBytecodeAdapter.java:648)
at org.projectregistry.services.GroovyToolsTest$_testNeededToolList_closure2.doCall(GroovyToolsTest.groovy:34)
...

Groovy输出中提供了更多信息,这反过来又使您可以更快地进行修复。

代码项目

因此,随着可以节省语法和输出的时间,并希望通过一种新的和不同的语言来增加编程乐趣,我希望每个人都可以尝试Groovy和/或Spock来克服惯性,这种惯性会阻止程序员进行单元测试。

学习如何简单。Groovy和Spock都有据可查的文档,仅通过搜索即可获得许多资源。在各种社交媒体上也有一个非常活跃和乐于助人的社区,我相信很乐意提供帮助。

技术类文章精选

非技术文章精选

人生苦短?试试Groovy进行单元测试的更多相关文章

  1. Groovy+Spock单元测试

    一.导入依赖 Spock是基于JUnit的单测框架,提供一些更好的语法,结合Groovy语言,可以写出更为简洁的单测. <!-- groovy依赖 --> <dependency&g ...

  2. 新学习的语言Groovy

    什么是 Groovy? Groovy 是 JVM 的一个替代语言 —替代 是指可以用 Groovy 在 Java 平台上进行 Java 编程,使用方式基本与使用 Java 代码的方式相同.在编写新应用 ...

  3. Spring Boot中使用MyBatis注解配置详解(1)

    之前在Spring Boot中整合MyBatis时,采用了注解的配置方式,相信很多人还是比较喜欢这种优雅的方式的,也收到不少读者朋友的反馈和问题,主要集中于针对各种场景下注解如何使用,下面就对几种常见 ...

  4. Spring Boot中使用MyBatis注解配置详解

    传参方式 下面通过几种不同传参方式来实现前文中实现的插入操作. 使用@Param 在之前的整合示例中我们已经使用了这种最简单的传参方式,如下: @Insert("INSERT INTO US ...

  5. Spring Boot教程(三十九)使用MyBatis注解配置详解(2)

    增删改查 MyBatis针对不同的数据库操作分别提供了不同的注解来进行配置,在之前的示例中演示了@Insert,下面针对User表做一组最基本的增删改查作为示例: public interface U ...

  6. Spring Boot 2.x基础教程:使用MyBatis访问MySQL

    之前我们已经介绍了两种在Spring Boot中访问关系型数据库的方式: 使用spring-boot-starter-jdbc 使用spring-boot-starter-data-jpa 虽然Spr ...

  7. Groovy单元测试框架spock数据驱动Demo

    spock是一款全能型的单元测试框架. 上次文章分享了spock框架的基础功能的使用,在此基础上,我根据自己写的Groovy的封装方法.数据驱动以及一些Groovy的高级语法做了一些尝试.发现还是有一 ...

  8. Groovy单元测试框架spock基础功能Demo

    spock是一款全能型的单元测试框架. 最近在做单元测试框架的调研和尝试,目前确定的方案框架包括是:spock,Junit,Mockito以及powermock.由于本身使用Groovy的原因,比较钟 ...

  9. 工作3年,还不会写单元测试?新技能get!

    历史遗留代码不敢重构? 每次改代码都要回归所有逻辑? 提测被打回? 在近期的代码重构的过程中,遇到了各式各样的问题.比如调整代码顺序导致bug,取反操作逻辑丢失,参数校验逻辑被误改等. 上线前需要花大 ...

随机推荐

  1. 【使用篇二】Quartz自动化配置集成(17)

    出处:https://www.jianshu.com/p/49133c107143 定时任务在企业项目比较常用到,几乎所有的项目都会牵扯该功能模块,定时任务一般会处理指定时间点执行某一些业务逻辑.间隔 ...

  2. Redis与Redis 伪集群环境的搭建

    一 .准备工作 GCC编译环境 ruby运行环境 安装ruby脚本运行包 二.环境安装 1.GCC环境 首先,因为redis是由C语言编写的,所以需要安装GCC环境,可以用 gcc -v 命令来检查是 ...

  3. C语言中,关于相除的问题

    若定义的类型是 整数型:int a,b ;则a/b的值为整数的,不会带着小数点的..... 若定义的数据类型是 浮点型的 :double a,b; 则这种情况下,得到的结果与数学结果无异

  4. 网络爬虫入门:你的第一个爬虫项目(requests库)

    0.采用requests库 虽然urllib库应用也很广泛,而且作为Python自带的库无需安装,但是大部分的现在python爬虫都应用requests库来处理复杂的http请求.requests库语 ...

  5. 一篇文章看懂angularjs component组件

     壹 ❀ 引 我在 angularjs 一篇文章看懂自定义指令directive 一文中详细介绍了directive基本用法与完整属性介绍.directive是个很神奇的存在,你可以不设置templa ...

  6. 如何使用1行代码让你的C++程序控制台输出彩色log信息

    本文首发于个人博客https://kezunlin.me/post/a201e11b/,欢迎阅读最新内容! colorwheel for colored print and trace for cpp ...

  7. AES 对称加密

    package com.skynet.rimp.common.utils.string; import java.io.UnsupportedEncodingException; import jav ...

  8. github仓库迁移到gitlab以及gitlab仓库迁移到另一个gitlab服务器

    一. github仓库迁移到gitlab 先进入 new project: 选择 Import project, 选择下面的github: 进入后,这里需要github的 personal acces ...

  9. .netcore控制台->定时任务Quartz

    之前做数据同步时,用过timer.window服务,现在不用那么费事了,可以使用Quartz,并且配置灵活,使用cron表达式配置XML就可以.我用的是3.0.7版本支持.netcore. 首先创建一 ...

  10. ABP入门教程7 - 基础设施层更新数据库

    点这里进入ABP入门教程目录 设置数据库 在基础设施层(即JD.CRS.EntityFrameworkCore)打开数据库环境设置 JD.CRS.EntityFrameworkCore/EntityF ...