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

但是,当我开始使用代码并问“单元测试在哪里?”时,我得到了一个完全不同的故事。我最近在网上问我的程序员朋友为什么不这样做,以及为什么其他程序员不这样做呢?不要编写单元测试。当我问程序员或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. GUI程序分析实例

    GUI程序开发概述 GUI程序开发原理 GetMessage(&msg)将消息队列中的消息取出来,在循环中进行处理. GUI程序开发的本质

  2. 必威电竞2019或将赞助SKT,携手Faker再创辉煌

    必威电竞yabo055点康母,这是一家相当优秀的竞技娱乐平台,平台涉及的领域也比较广泛,包括各类电子竞技游戏以及相关资讯,平台内有很多专家,每日为大家分享各类热门赛事等一些游戏攻略.现在的电子竞技发展 ...

  3. Attention 和self-attention

    1.Attention 最先出自于Bengio团队一篇论文:NEURAL MACHINE TRANSLATION BY JOINTLY LEARNING TO ALIGN AND TRANSLATE ...

  4. 给OPi Zero Plus添加USB启动功能

    为使OPi Zero Plus支持U盘启动,需要在板载的SPI Flash当中刷入uboot.在这个过程当中绕了很多弯路,特此记录 最终操作步骤见文末 网上的教程仅使用sudo modprobe sp ...

  5. dom元素的tabindex属性介绍及在vue项目中的应用

    dom元素的tabindex属性介绍及在vue项目中的应用 tabindex属性作用 让普通dom元素变为可聚焦的元素 让普通dom元素可以参与顺序键盘导航(通常使用Tab键,因此得名). tabin ...

  6. vue 父子父组件通过props传父页面请求后的数据

    父子父组件通过props传父页面请求后的数据,则在父页面的子组件上加上判断数据是否存在即可,如下 <gl-line-bar v-if="oneWeekBetEcharts" ...

  7. C++ 面向对象程序设计复习大纲

     这是我在准备C++考试时整理的提纲,如果是通过搜索引擎搜索到这篇博客的师弟师妹,建议还是先参照PPT和课本,这个大纲也不是很准确,自己总结会更有收获,多去理解含义,不要死记硬背,否则遇到概念辨析题会 ...

  8. ZooKeeper(三):请求处理链路的创建过程解析

    我们知道,zk就是一个个处理链组成的. 但是,这些处理链是在什么创建的呢? ZooKeeper 中有三种角色的服务节点存在: Leader, Follower, Observer . 而每个服务节点的 ...

  9. 练手爬虫用urllib模块获取

    练手爬虫用urllib模块获取 有个人看一段python2的代码有很多错误 import re import urllib def getHtml(url): page = urllib.urlope ...

  10. 最小割最大流定理&残量网络的性质

    最小割最大流定理的内容: 对于一个网络流图 $G=(V,E)$,其中有源点和汇点,那么下面三个条件是等价的: 流$f$是图$G$的最大流 残量网络$G_f$不存在增广路 对于$G$的某一个割$(S,T ...