abstract:
登录-失败!:
1. <9.1.3>”编辑失败“测试
test "unsuccessful edit" do
get edit_user_path(@user)
patch user_path(@user), user: { name: '',
email: '',
password: 'foo',
password_confirmation: 'bar' }
assert_template 'users/edit'
end
测试结果:
moses@moses-virtual-machine:~/projects/compass_v1.1$ bundle exec rake test
Started
FAIL["test_unsuccessful_edit", UsersEditTest, 0.604847775]
test_unsuccessful_edit#UsersEditTest (0.60s)
expecting <"users/edit"> but rendering with <[]>
test/integration/users_edit_test.rb:17:in `block in <class:UsersEditTest>'
14/14: [=================================] 100% Time: 00:00:00, Time: 00:00:00
Finished in 0.70103s
14 tests, 37 assertions, 1 failures, 0 errors, 0 skips
2. <9.1.4>”编辑成功“测试
test "successful edit" do
get edit_user_path(@user)
name = "Foo Bar"
email = "foo@bar.com"
patch user_path(@user), user: { name: name,
email: email,
password: "",
password_confirmation: "" }
assert_not flash.empty?
assert_redirected_to @user
@user.reload
assert_equal @user.name, name
assert_equal @user.email, email
end
测试结果:
moses@moses-virtual-machine:~/projects/compass_v1.1$ bundle exec rake test
Started
FAIL["test_successful_edit", UsersEditTest, 0.238506237]
test_successful_edit#UsersEditTest (0.24s)
Expected response to be a redirect to <http://www.example.com/users/762146111> but was a redirect to <http://www.example.com/login>.
Expected "http://www.example.com/users/762146111" to be === "http://www.example.com/login".
test/integration/users_edit_test.rb:19:in `block in <class:UsersEditTest>'
14/14: [=================================] 100% Time: 00:00:00, Time: 00:00:00
Finished in 0.67668s
14 tests, 39 assertions, 1 failures, 0 errors, 0 skips
错误1和错误2涉及到8.4.6节的log_in_as方法
注册信息有效性测试:
1. 邮件格式测试
test/models/user_test.rb
require 'test_helper'
class UserTest < ActiveSupport::TestCase
def setup
@user = User.new(name: "Example User", email: "user@example.com")
end
.
.
.
test "email validation should accept valid addresses" do
valid_addresses = %w[user@example.com USER@foo.COM A_US-ER@foo.bar.org
first.last@foo.jp alice+bob@baz.cn]
valid_addresses.each do |valid_address|
@user.email = valid_address
assert @user.valid?, "#{valid_address.inspect} should be valid"
end
end
end
使用%w[]创建“邮箱地址”的数组
assert @user.valid?, "#{valid_address.inspect} should be valid"
中inspect 方法,返回被调用对象的字符串字面量表现形式
例如:
>> puts (1..5).to_a # 把值域转换成数组
1
2
3
4
5
>> puts (1..5).to_a.inspect # 输出数组的字面量形式
[1, 2, 3, 4, 5]
>> puts :name, :name.inspect
name
:name
>> puts "It worked!", "It worked!".inspect
It worked!
"It worked!"
2. 唯一性验证
拒绝重复电子邮件地址的测试 RED
test/models/user_test.rb
require 'test_helper'
class UserTest < ActiveSupport::TestCase
def setup
@user = User.new(name: "Example User", email: "user@example.com")
end
.
.
.
test "email addresses should be unique" do
duplicate_user = @user.dup
@user.save
assert_not duplicate_user.valid?
end
end
这里使用@user.dup 方法创建一个和 @user 的电子邮件地址一样的用户对象,然后保存 @user,因为数据库中的 @user 已经占用了这个电子邮件地址,所有 duplicate_user 对象无效。
注册测试:
生成测试文件:
$ rails generate integration_test users_signup
进行测试:
$ bundle exec rake test
1. 注册失败的测试
第一个测试文件:
test/integration/users_signup_test.rb
require 'test_helper'
class UsersSignupTest < ActionDispatch::IntegrationTest
test "invalid signup information" do
get signup_path
assert_no_difference 'User.count' do
post users_path, user: { name: "",
email: "user@invalid",
password: "foo",
password_confirmation: "bar" }
end
assert_template 'users/new'
end
end
get 方法访问注册页面
get signup_path
用assert_no_difference方法判断提交的数据是否改变
assert_no_difference 'User.count'
即,User.count有没有增加,借此来反应用户注册有没有成功。
2. 注册成功的测试
第二个测试文件:
test/integration/users_signup_test.rb
require 'test_helper'
class UsersSignupTest < ActionDispatch::IntegrationTest
.
.
.
test "valid signup information" do
get signup_path
name = "Example User"
email = "user@example.com"
password = "password"
assert_difference 'User.count', 1 do
post_via_redirect users_path, user: { name: name,
email: email,
password: password,
password_confirmation: password }
end
assert_template 'users/show'
end
end
对比第一个测试文件
assert_no_difference 'User.count' do
post users_path, ...
end
第二个测试文件中的assert_difference方法
assert_difference 'User.count', 1 do
post_via_redirect users_path, ...
end
指的是观察“指定变化的数量”,第二个参数可选,这里是1。
post_via_redirect 方法,目的是提交数据后继续跟踪重定向
登录测试:
生成测试文件:
$ rails generate integration_test users_login
测试指定文件:指定 TEST 参数和文件的完整路径,演示如何只运行一个测试文件
$ bundle exec rake test TEST=test/integration/users_login_test.rb
1. 注册失败的测试
捕获继续显示闪现消息的测试
test/integration/users_login_test.rb
require 'test_helper'
class UsersLoginTest < ActionDispatch::IntegrationTest
test "login with invalid information" do
get login_path
assert_template 'sessions/new'
post login_path, session: { email: "", password: "" }
assert_template 'sessions/new'
assert_not flash.empty?
get root_path
assert flash.empty?
end
end
assert_template 方法检查首页是否使用正确的视图渲染
assert_not 方法确认得到的用户对象是无效的
2. 测试布局中的变化
测试步骤:
1. 访问登录页面;
2. 通过 post 请求发送有效的登录信息;
3. 确认登录链接消失了;
4. 确认出现了退出链接;
5. 确认出现了资料页面链接。
为了检查这些变化,在测试中要登入已经注册的用户,也就是说数据库中必须有一个用户。Rails 默认使用“固件”实现这种需求。固件是一种组织数据的方式,这些数据会载入测试数据库。
目前,我们只需要一个用户,它的名字和电子邮件地址应该是有效的。因为我们要登入这个用户,所以还要提供正确的密码,和提交给会话控制器中 create 动作的密码比较。
要先理解:
password_digest
密码摘要使用 bcrypt 生成(通过 has_secure_password 方法),所以固件中的密码摘要也要使用这种方法生成。查看安全密码的源码后,我们发现生成摘要的方法是:
BCrypt::Password.create(string, cost: cost)
其中,string 是要计算哈希值的字符串;cost 是“耗时因子”,决定计算哈希值时消耗的资源。耗时因子的值越大,由哈希值破解出原密码的难度越大。这个值对生产环境的安全防护很重要,但在测试中我们希望 digest 方法的执行速度越快越好。安全密码的源码中还有这么一行代码:
cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST :
BCrypt::Engine.cost
这行代码的作用是严格实现前面的分析:在测试中耗时因子使用最小值,在生产环境则使用普通(最大)值。
digest 方法可以放在几个不同的地方,这里放在 user.rb 中。
# 返回指定字符串的哈希摘要
def User.digest(string)
cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST :
BCrypt::Engine.cost
BCrypt::Password.create(string, cost: cost)
end
定义好 digest 方法后,我们可以创建一个有效的用户固件了
测试用户登录所需的固件
test/fixtures/users.yml
michael:
name: Michael Example
email: michael@example.com
password_digest: <%= User.digest('password') %>
使用
<%= User.digest('password') %>
创建密码摘要
我们虽然定义了 has_secure_password 所需的 password_digest 属性,但有时也需要使用密码的原始值。可是,在固件中无法实现,如果添加 password 属性,Rails 会提示数据库中没有这个列(确实没有)。所以,我们约定固件中所有用户的密码都一样,即 ‘password’。
创建了一个有效用户固件后,在测试中可以使用下面的方式获取这个用户:
user = users(:michael)
测试使用有效信息登录的情况
test/integration/users_login_test.rb
require 'test_helper'
class UsersLoginTest < ActionDispatch::IntegrationTest
def setup
@user = users(:michael)
end
.
.
.
test "login with valid information" do
get login_path
post login_path, session: { email: @user.email, password: 'password' }
# assert_redirected_to @user 检查重定向的地址是否正确;
assert_redirected_to @user
# follow_redirect! 访问重定向的目标地址。
follow_redirect!
assert_template 'users/show'
# 测试login_path链接数为0
assert_select "a[href=?]", login_path, count: 0
# 测试logout_path链接数为1
assert_select "a[href=?]", logout_path
assert_select "a[href=?]", user_path(@user)
end
end