- 学习 Ruby on Rails
- Rails 2.1 主页
- Rails 2.1 简介
- 导轨 2.1 安装
- Rails 2.1 框架
- Rails 2.1 目录结构
- Rails 2.1 示例
- Rails 2.1 数据库设置
- Rails 2.1 活动记录
- Rails 2.1 迁移
- Rails 2.1 控制器
- Rails 2.1 视图
- Rails 2.1 布局
- Rails 2.1 脚手架
- Rails 2.1 和 AJAX
- Rails 2.1 上传文件
- Rails 2.1 发送电子邮件
- 高级 Ruby on Rails 2.1
- Rails 2.1 RMagick 指南
- Rails 2.1 基本 HTTP 身份验证
- Rails 2.1 错误处理
- Rails 2.1 路线系统
- Rails 2.1 单元测试
- 高级 Ruby on Rails 2.1
- Rails 2.1 提示与技巧
- 快速参考指南
- 快速参考指南
- Ruby on Rails 2.1 有用资源
- Ruby on Rails 2.1 - 资源
- Ruby on Rails 2.1 - 讨论
Ruby on Rails 2.1 - 单元测试
介绍
在继续之前,让我们快速浏览一下一些定义 -
测试- 它们是测试应用程序,可以产生一致的结果并证明 Rails 应用程序执行了预期的操作。测试与实际应用同时开发。
断言- 这是一行代码,用于评估对象(或表达式)以获得预期结果。例如——这个值=那个值吗?这个对象为零吗?
测试用例- 这是一个从 Test::Unit::TestCase 继承的类,包含由上下文相关的测试组成的测试策略。
测试套件- 这是测试用例的集合。当您运行测试套件时,它将依次执行属于它的每个测试。
轨道测试
当您运行帮助程序脚本script/generate来创建控制器和模型时,Rails 会生成一个用于单元和功能测试的框架。通过在框架中填充针对您编写的功能的测试,您可以获得相当好的测试覆盖率。在 Rails 应用程序中有两个重要的测试点 -
测试模型
测试控制器
本教程将简要介绍这两项测试。因此,让我们创建一个测试应用程序来理解这一概念。
C:\ruby> rails -d mysql testapp
数据库设置
到目前为止,我们仅使用了 Rails 应用程序的开发数据库,但现在您需要确保还创建了测试数据库,并且正确设置了 config/database.yml 文件的相应部分。
让我们创建开发和测试数据库,如下所示 -
mysql> create database testapp_test; Query OK, 1 row affected (0.01 sec) mysql> create database testapp_development; Query OK, 1 row affected (0.01 sec) mysql> use testapp_test; Database changed mysql> grant all privileges on testapp_test.* to 'root'@'localhost' identified by 'password'; Query OK, 0 rows affected (0.00 sec) mysql> FLUSH PRIVILEGES; Query OK, 0 rows affected (0.00 sec)
配置数据库.yml
配置您的 config/database.yml 如下 -
development: adapter: mysql encoding: utf8 database: testapp_development username: root password: password host: localhost test: adapter: mysql encoding: utf8 database: testapp_test username: root password: password host: localhost production: adapter: mysql encoding: utf8 database: testapp_production username: root password: password host: localhost
生成迁移
假设您有一个包含书籍的表,其中包括书籍的标题、价格和简短的描述。以下迁移设置了该表 -
testapp > ruby script/generate migration books
现在修改 testapp/db/migrate/20080616170315_books.rb 文件如下 -
class Books < ActiveRecord::Migration def self.up create_table :books do |t| t.string :title, :limit => 32, :null => false t.float :price t.text :description t.timestamp :created_at end end def self.down drop_table :books end end
现在运行迁移如下 -
testapp > rake db:migrate
这将在 testapp_development 数据库中创建books表。此后,我们需要使用rake命令设置测试数据库,如下所示 -
C:\ruby\testapp > rake db:test:clone_structure
这会将testapp_development数据库克隆到testapp_test数据库中。这意味着无论您在开发数据库中拥有什么,现在您在测试数据库中也将拥有相同的数据。
测试模型
当您使用生成脚本生成模型时,Rails 还会在测试目录中为模型生成单元测试脚本。它还创建一个固定装置,一个包含要加载到 testapp_test 数据库中的测试数据的YAML文件。这是您的单元测试将运行的数据 -
testapp > ruby script/generate model Book exists app/models/ exists test/unit/ exists test/fixtures/ create app/models/book.rb create test/unit/book_test.rb create test/fixtures/books.yml create db/migrate create db/migrate/20080616164236_create_books.rb
当您在模型类中编写代码时,您将在这些文件中编写相应的测试。因此,让我们在 test/fixtures/books.yml 中使用 YAML 创建两个测试书记录,如下所示 -
perl_cb: id: 1 title: 'Ruby Tutorial' price: 102.00 description : 'This is a nice Ruby tutorial' java_cb: id: 2 title: 'Java Programming' price: 62.00 description : 'Java Programming for the beginners'
现在让我们用以下代码替换书籍单元测试文件 test/unit/book_test.rb 中的现有代码 -
require File.dirname(__FILE__) + '/../test_helper' class BookTest < ActiveSupport::TestCase fixtures :books def test_book perl_book = Book.new :title => books(:perl_cb).title, :price => books(:perl_cb).price, :description => books(:perl_cb).description, :created_at => books(:perl_cb).created_at assert perl_book.save perl_book_copy = Book.find(perl_book.id) assert_equal perl_book.title, perl_book_copy.title perl_book.title = "Ruby Tutorial" assert perl_book.save assert perl_book.destroy end end
最后,运行测试方法如下 -
testapp > ruby test/unit/book_test.rb
这是运行成功测试用例的输出 -
testapp > ruby test/unit/book_test_crud.rb Loaded suite ./test/unit/book_test Started . Finished in 0.0625 seconds. 1 tests, 4 assertions, 0 failures, 0 errors
让我们分析一下这里发生了什么 -
BookTest 方法首先使用文本fixture/books.yml 中第一条记录的标题和其他字段创建一个新的Book 对象。生成的对象存储在 perl_book 实例变量中。
第一个断言测试保存 Book 对象是否成功。
接下来,使用find方法检索 book 对象并将其存储在另一个名为 perl_book_copy 的实例变量中。此检索是否成功将在下一个断言中进行测试,该断言将比较两个图书对象的标题。至此,我们已经测试了创建和读取数据库记录的能力。
该解决方案通过为 perl_book 中存储的对象分配新标题来测试更新,然后断言保存更改成功。
最后,测试销毁 Book 对象的能力。
这就是我们测试 Rails 模型的方法。
测试控制器
控制器测试也称为功能测试。功能测试测试控制器的以下类型的功能 -
- 响应是否按预期重定向?
- 是否呈现了预期的模板?
- 路由是否符合预期?
- 响应是否包含预期的标签?
Rails 框架支持五种类型的请求 -
- 得到
- 邮政
- 放
- 头
- 删除
要编写功能测试,您需要模拟控制器将处理的五种 HTTP 请求类型中的任何一种。
请求类型“get”和“post”是控制器测试中最常用的。所有这些方法都有四个参数 -
- 控制器的动作
- 请求参数的可选哈希值
- 可选的会话哈希
- 可选的闪存哈希
在本教程中,我们将了解如何使用get方法来测试我们的控制器。您可以以类似的方式测试其余方法。
当您使用generate生成控制器时,Rails会为控制器创建一个功能测试脚本,如下所示 -
testapp > ruby script/generate controller Book exists app/controllers/ exists app/helpers/ create app/views/book exists test/functional/ create app/controllers/book_controller.rb create test/functional/book_controller_test.rb create app/helpers/book_helper.rb
当您在控制器类中编写代码时,您将在这些文件中编写相应的测试。在此之前,让我们在app/controllers/book_controller.rb中定义控制器功能列表、显示和搜索,如下所示 -
class BookController < ApplicationController def list @book_pages, @books = paginate :books, :per_page => 10 end def show @book = Book.find(params[:id]) end def search @book = Book.find_by_title(params[:title]) if @book redirect_to :action => 'show', :id => @book.id else flash[:error] = 'No such book available' redirect_to :action => 'list' end end end
注意- 您需要两个视图模板用于显示和列表方法。您可以定义这些视图并测试它们,但现在,我们将继续而不定义这些视图。
现在让我们重用test/fixtures/books.yml文件中的测试装置,如下所示 -
perl_cb: id: 1 title: 'Ruby Tutorial' price: 102.00 description : 'This is a nice Ruby tutorial' java_cb: id: 2 title: 'Java Programming' price: 62.00 description : 'Java Programming for the beginners'
将以下test_search_book和test_search_not_found方法添加到test/function/book_controller_test.rb以测试 Book Controller 搜索操作的功能。
require File.dirname(__FILE__) + '/../test_helper' require 'book_controller' # Re-raise errors caught by the controller. class BookController def rescue_action(e) raise e end end class BookControllerTest < Test::Unit::TestCase fixtures :books def setup @controller = BookController.new @request = ActionController::TestRequest.new @response = ActionController::TestResponse.new end def test_search_book get :search, :title => 'Ruby Tutorial' assert_not_nil assigns(:book) assert_equal books(:perl_cb).title, assigns(:book).title assert_valid assigns(:book) assert_redirected_to :action => 'show' end def test_search_not_found get :search, :title => 'HTML Tutorial' assert_redirected_to :action => 'list' assert_equal 'No such book available', flash[:error] end end
现在运行您的测试用例如下 -
testapp > ruby test/functional/book_controller_test.rb
它给出以下输出 -
Loaded suite test/functional/book_controller_test Started .. Finished in 0.422 seconds. 2 tests, 7 assertions, 0 failures, 0 errors
让我们分析一下这里发生了什么 -
setup方法是创建控制器、请求和响应对象的默认方法。它们将由 Rails 内部使用。
第一个测试方法test_search_book生成对搜索操作的get请求,并传入标题参数。
接下来的两个断言验证Book对象是否保存在名为@book 的实例变量中,以及该对象是否通过了可能存在的任何 Active Record 验证。
第一个方法中的最终断言测试请求是否已重定向到控制器的显示操作。
第二个测试方法test_search_not_found执行另一个get请求,但传入无效标题
第一个断言测试是否发出了到列表操作的重定向。
如果正在进行的断言通过,则闪存哈希中应该有一条消息,您可以使用assert_equal 进行测试。
要获取有关断言的更多信息,请参阅Rails 标准文档。
使用 Rake 进行测试
您可以使用rake实用程序来测试您的应用程序。下面给出了一些重要命令的列表。
$rake test - 测试所有单元测试和功能测试(以及集成测试,如果存在)。
$rake test:functions - 运行所有功能测试。
$rake test:units - 运行所有单元测试。
$rake test:integration - 运行所有集成测试。
$rake test:plugins - 运行 ./vendor/plugins/**/test 中的所有测试。
$rake test:recent - 对过去 10 分钟内修改过的模型和控制器运行测试 -
$rake test:uncompaid - 对于 Subversion 中的项目,对自上次提交以来模型和控制器中发生的更改运行测试 -