精通 Grails: 测试 Grails 应用程序
【IT168技术文档】我是测试驱动开发(test-driven development,TDD)的大力支持者。Neal Ford(The Productive Programmer 的作者)说道 “不测试所编写的代码就是失职”。Michael Feathers(Working Effectively with Legacy Code 的作者)将 “遗留代码” 定义为没有经过相应测试的任何软件 — 这表明编写代码而不进行测试是一种过时的实践。我常说每编写一定数量的生产代码,就要编写两倍的测试代码。
精通 Grails 尚未讨论 TDD,因为到目前为止,这个系列主要关注如何利用 Grails 的核心功能。测试基础设施代码(不用您编写的代码)有一定的价值,但我很少这样做。我相信 Grails 能够正确地将我的 POGO 呈现为 XML,或在我调用 trip.save() 时将我的 Trip 保存到数据库。当您检查自己编写的代码时,测试的真正价值就体现出来了。如果您编写一个复杂的算法,您应该有一个或多个补充单元测试,确保该算法正常工作。在本文,您将看到 Grails 如何帮助和鼓励您进行应用程序测试。
编写第一个测试
在开始测试之前,我将介绍一个新的域类。这个类的一些定制功能必须经过测试才能进入到生产中。输入 grails create-domain-class HotelStay,如清单 1 所示:
清单 1. 创建 HotelStay 类
Environment set to development
[copy] Copying 1 file to /src/trip-planner2/grails-app/domain
Created Domain Class for HotelStay
[copy] Copying 1 file to /src/trip-planner2/test/integration
Created Tests for HotelStay
从清单 1 可以看到,Grails 在 grails-app/domain 目录中为您创建了一个空的域类。它还在 test/integration 目录中创建了一个带有空的 testSomething() 方法的 GroovyTestCase 类(稍后我将进一步讲述单元测试和集成测试的区别)。清单 2 展示了一个带有生成的测试的空 HotelStay 类:
清单 2. 带有生成的测试的空类
}
class HotelStayTests extends GroovyTestCase {
void testSomething() {
}
}
GroovyTestCase 是在 JUnit 3.x 单元测试之上的一层 Groovy。如果您熟悉 JUnit TestCase,您肯定知道 GroovyTestCase 是如何工作的。对于这两种情况,您通过断言代码正常工作来测试它们。JUnit 有各种不同的断言方法,包括 assertEquals、assertTrue 和 assertNull 等等。它使您通过编程的方式表明 “我断言这个代码按照预期工作”。
将清单 3 中的代码添加到 grails-app/domain/HotelStay.groovy 和 test/integration/HotelStayTests.groovy:
清单 3. 一个简单的测试
String hotel
}
class HotelStayTests extends GroovyTestCase {
void testSomething(){
HotelStay hs = new HotelStay(hotel:"Sheraton")
assertEquals "Sheraton", hs.hotel
}
}
清单 3 正是我前面提到那种低级 Grails 基础设施测试。您应该相信 Grails 能够正确执行这个操作,因此这是一个典型的错误测试类型。但它允许您编写最简单的测试并观察其运行,实现了本文的目的。
要运行所有测试,请输入 grails test-app。要仅运行这个测试,请输入 grails test-app HotelStay(由于约定优于配置,Tests 后缀可以省略)。不管输入哪个命令,您应该会在命令提示中看到如清单 4 所示的输出(注意:为了突出重要的特性,我删减了许多代码)。
清单 4. 运行测试时的输出
Environment set to test
No tests found in test/unit to execute ...
-------------------------------------------------------
Running 1 Integration Test...
Running test HotelStayTests...
testSomething...SUCCESS
Integration Tests Completed in 253ms
-------------------------------------------------------
Tests passed. View reports in /src/trip-planner2/test/reports
这里发生了 4 件重要的事情:
1.可以看到,environment 被设置为 test。这意味着 conf/DataSource.groovy 文件中的 test 块的数据库设置已生效。
2.test/unit 中的脚本已运行。您尚未编写任何单元测试,所以不能找到任何单元测试,这并不奇怪。
3.test/integration 中的脚本已经运行。您可以看到 HotelStayTests.groovy 脚本的输出 — 它的旁边有个很大的 SUCCESS。
4.这个脚本向您展示一组报告。
如果您在 Web 浏览器中打开 /src/trip-planner2/test/reports/html/index.html,应该会看到一个关于所有已运行的测试的报告。如图 1 所示。
图 1. JUnit 优异汇总报告

如果您单击 HotelStayTests 链接,应该会看到 doSomething() 测试,如图 2 所示:
图 2. JUnit 类级报告

如果测试意外失败,命令提示输出和 HTML 报告(如图 3 所示)将通知您:
图 3. 失败的 JUnit 测试
