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

但是,当我开始使用代码并问“单元测试在哪里?”时,我得到了一个完全不同的故事。我最近在网上问我的程序员朋友为什么不这样做,以及为什么其他程序员不这样做呢?不要编写单元测试。当我问程序员或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. pytest系列(四)- pytest+allure+jenkins - 持续集成平台生成allure报告

    pytest是什么 pytest是python的一款测试框架,拥有unittest的功能并比它更丰富. allure是什么 有非常多的优秀的测试框架,但却是有非常少优秀的报告工具可以展示非常清楚的用例 ...

  2. R-4 方差分析

    本节内容: 1:方差分析的原理 2:单因数方差分析 .双因数分析 3:交互项 一:方差分析是原理 方差分析原理 对总体均值的假设检验,有三种情况:1.总体均值与某个常数进行比较:2.两个总体均值之间的 ...

  3. nmcli详解

    1. nmcli 安装 [root@liujunjun ~]# yum install -y NetworkManager CentOS7默认已安装了 2. nmcli 基本选项 选项 作用 -t 简 ...

  4. 浅谈python面向对象编程和面向过程编程的区别

    面向过程:分析出解决问题所需要的步骤,然后用函数把这些步骤一步步实现,使用的时候再一个个的依次调用即可. 优点:性能高 缺点:相较于面向对象而言,不易维护,不易复用,不易扩展 适合于小型的项目面向对象 ...

  5. H5移动端开发遇见的东西

    常见的有viewport.强制浏览器全屏.IOS的Web APP模式.可点击元素出现阴影 本文主要讲一些其他的或者实用的优化手段. 1. 弹出数字键盘 <!-- 有"#" & ...

  6. AES 对称加密

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

  7. 解决maven创建web项目过慢的问题

    在骨架选择完成后,变量处. 增加一组变量 archetypeCatalog internal

  8. 使用Redis实现延时任务(二)

    前提 前一篇文章通过Redis的有序集合Sorted Set和调度框架Quartz实例一版简单的延时任务,但是有两个相对重要的问题没有解决: 分片. 监控. 这篇文章的内容就是要完善这两个方面的功能. ...

  9. IL指令列表

    使用编译器可以将C#代码编译为中间语言(Intermediate Language,IL)代码,中间语言是一种平台无关的指令集,最终会由CLR将中间语言字节码转换为对应平台的机器码从而执行:阅读IL代 ...

  10. vue v-if和v-show的区别

    其中v-if依赖于控制DOM节点,而v-show是依赖于控制DOM节点的display属性. 当v-show传入的值为true时,对应DOM元素的display的值为block之类的,反之为false ...