首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

精通 Grails 使用 Grails 进行单元测试(1)

精通 Grails 使用 Grails 进行单元测试(1)

Grails 支持两种基本的测试类型:单元测试和集成测试。两种测试的语法完全相同:都被使用相同的断言编写为一个 GroovyTestCase。它们之间的区别在于语义上。单元测试用于在隔离环境下测试类,而集成测试支持在完整的、正在运行的环境中测试类。
该文章是根据当时最新的 Grails 1.0 版本编写的,在该版本中,测试基础架构的功能得到了显著改进。GrailsUnitTestCase 类及其子类的引入将流程测试的简单性和全面性提升到了一个全新的水平。具体来讲,这些新测试类的模拟功能提升了单元测试的速度,同时能够像在集成测试中一样正常测试功能。图 1 展示了 Grails 1.1.x 中全新的测试层次结构:
图 1. Grails 1.1.x 中全新的测试层次结构当您在下一节中创建一个新的域类和控制器时,您将了解如何实际应用 GrailsUnitTestCase 和 ControllerUnitTestCase。(本文示例的完整源代码可 下载 获得)。
开始要执行本文中的示例,首先创建一个新应用程序。在命令提示符下键入:
1
grails create-app testing




更改到测试目录(cd testing),然后键入:
1
grails create-domain-class User




接下来键入:
1
grails create-controller User




将清单 1 中的代码添加到 grails-app/domain/User.groovy 中:
清单 1. User 域类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class User {
  String name
  String login
  String password
  String role = "user"

  static constraints = {
    name(blank:false)
    login(unique:true, blank:false)
    password(password:true, minSize:5)
    role(inList:["user", "admin"])
  }

  String toString(){
    "${name} (${role})"
  }
}




定义 grails-app/controller/UserController.groovy 的核心行为,如清单 2 所示:
清单 2. UserController 类
1
2
3
class UserController {
    def scaffold = true
}




现在基本的基础架构已经就绪了,接下来添加一些测试。
在 GrailsUnitTestCase 中进行模拟在文本编辑器中打开 test/unit/UserTests.groovy。代码如清单 3 所示:
清单 3. UserTests 类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import grails.test.*

class UserTests extends GrailsUnitTestCase {
    protected void setUp() {
        super.setUp()
    }

    protected void tearDown() {
        super.tearDown()
    }

    void testSomething() {

    }
}




在 Grails 1.0 中,create-domain-class 命令创建的存根测试扩展了 GroovyTestCase。可以看到,现在对一个域类的单元测试(在 Grails 1.1 中)扩展了 GrailsUnitTestCase。所以,您可以使用一些新方法来在单元测试中启用模拟功能,这种功能在以前需要在集成测试中启用。
具体来讲,GrailsUnitTestCase 提供了以下模拟方法:
  • mockForConstraintsTests()
  • mockDomain()
  • mockLogging()
要理解这些模拟方法有何用途,首先创建一个会失败的测试。将 testSomething() 方法更改为 testBlank() 方法,如清单 4 所示:
清单 4. 一个将会失败的测试
1
2
3
4
void testBlank() {
  def user = new User()
  assertFalse user.validate()
}




您可能会问这个测试为什么会失败,毕竟它的语法是正确的。答案是您现在运行的是单元测试。单元测试意味着在隔离环境中运行,所以不会运行数据库和 Web 服务器,最重要的是不会发生与 Grails 相关的元编程。
回头看一下  中 User 域类的源代码,很明显其中没有定义任何 validate() 方法。此方法(以及 save()、list()、hasErrors() 和您熟悉的所有其他 Groovy Object Relational Mapping (GORM) 方法)都会被 Grails 在运行时动态添加到域类中。
要运行这个将会失败的测试,在命令提示符处键入 grails test-app。您应该看到清单 5 所示的结果:
清单 5. 控制台输出中显示的失败测试
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
$ grails test-app
Environment set to test

Starting unit tests ...
Running tests of type 'unit'
-------------------------------------------------------
Running 2 unit tests...
Running test UserControllerTests...PASSED
Running test UserTests...
                    testBlank...FAILED
Tests Completed in 1434ms ...
-------------------------------------------------------
Tests passed: 1
Tests failed: 1
-------------------------------------------------------

Starting integration tests ...
Running tests of type 'integration'
No tests found in test/integration to execute ...

Tests FAILED - view reports in /testing/test/reports.




在查看失败报告之前,您是否注意到单元测试运行速度很快,而在运行集成测试时会有明显的延迟?键入 grails test-app -unit 运行单元测试。即使测试仍然失败了,您也应该会看到测试运行速度上的显著改进。
当然,您可以键入 grails test-app -integration 来仅运行集成测试。事实上,您甚至可以将具有单元和集成标志与测试类的名称组合在一起。键入 grails test-app -unit User 定位到您感兴趣的特定测试类。(注意,您在名称后面省略了 Tests 后缀,能键入更少的内容始终是一件好事)。在现实世界中,将测试限制到单个类的能力能够使您对编写测试充满信心。
知道您拥有一个失败的测试之后,您可能希望查看错误消息。在 Web 浏览器中打开 test/reports/html/index.html。单击失败的测试类。将会看到如图 2 所示的结果:
图 2. 报告显示了失败的单元测试No signature of method: User.validate() 错误消息证实,Grails 确实没有将 validate() 方法元编程到 User 类上。
现在,您拥有两个选择。第一个选择是将此测试类转移到集成目录中。但是 Grails 转向运行集成测试需要很长时间,所以此选择不太理想。第二个选择是模拟验证行为并将测试类保留在单元目录中。
返回列表