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

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

    平常我们使用的Shader有顶点着色器.几何着色器.片段着色器,这几个都是为光栅化图形渲染服务的,OpenGL 4.3之后新出了一个Compute Shader,用于通用计算并行加速,现在对其进行介绍 ...

  2. PyCharm2019 激活

    文章末尾补充几个激活码:网上收集 一.破解补丁激活优点:永久期限 缺点:需要修改配置文件和下载破解文件 1.下载破解文件点击链接 链接: https://pan.baidu.com/s/1T405JC ...

  3. ej3-0开端

    开始 编码多年,总有一些最佳实践,Java也是,比如设计模式,比如Effective Java 3 (ej3) . 设计模式先后看过<大话设计模式>,<HeadFirst 设计模式& ...

  4. linux自建https证书

    一.生成单向认证的https证书 建立服务器私钥,生成RSA秘钥. 会有两次要求输入密码, 然后获得了一个server.key文件. 以后使用此文件(通过openssl提供的命令或API)可能经常回要 ...

  5. Shape.Type属性名称及对应值列表

    在Excel工作表中,有多种Shape类型的时候,可以通过shape.Type属性值返回一个代表形状类型的MsoShapeType数值.通过该数值可知该图形的类型! 列表如下: 名称 值 说明 mso ...

  6. 使用过Redis,我竟然还不知道Rdb

    目录 使用过Redis,那就先说说使用过那些场景吧 Rdb文件是什么,它是干什么的 分析工具 小结 联想 推荐阅读 使用过Redis,那就先说说使用过那些场景吧 字符串缓存 //举例 $redis-& ...

  7. Spring Boot可执行Jar包运行原理

    目录 1. 打可执行Jar包 2. 可执行Jar包内部结构 3. JarLauncher 4. 简单总结 5. 远程调试 Spring Boot有一个很方便的功能就是可以将应用打成可执行的Jar.那么 ...

  8. Pycharm自带Git实现版本管理

    之前一直使用本地的git客户端,通过命令来上传.下载代码到Gitlab:每次都需要启动git客户端,敲git命令来完成,不够灵活,因为强大的Pycharm就自带git功能,可以直接在Pycharm完成 ...

  9. Specify Action Settings 指定按钮设置

    In this lesson, you will learn how to modify Action properties. The ClearTasks Action will be used. ...

  10. Java日期处理组件joda-time

    版权声明:本文为xing_star原创文章,转载请注明出处! 本文同步自http://javaexception.com/archives/175 Java日期处理组件joda-time 平常在开发过 ...