首先,实践已经证明,大多数网站的用户验证系统都要对第三方代码库做一些定制和修改,这往往比重新开发一个验证系统的工作量还大。再者,现有的方案就像一个“黑盒”,你无法了解其中到底有些什么功能,而自己开发的话就能更好的理解实现的过程。而且,Rails 最近的更新,使开发验证系统变得很简单。最后,如果后续开发要用第三方代码库的话,因为自己开发过,所以你可以更好的理解其实现过程,便于定制功能。
创建“测试数据库”的正确命令:
$ bundle exec rake test:prepare
为某一列建立索引就需要改变数据库模型,在Rails中,这可以通过数据库迁移来实现。
let 方法
我们可以使用 RSpec 提供的 let 方法便捷的在测试中定义局部变量。let 方法的句法看起来有点怪,不过和变量赋值语句的作用是一样的。let 方法的参数是一个 Symbol,后面可以跟着一个块,块中代码的返回值会赋给名为 Symbol 代表的局部变量。也就是说:
let(:found_user) { User.find_by(email: @user.email) }
定义了一个名为 found_user 的变量,其值等于 find_by 的返回值。在这个测试用例的任何一个 before 或 it 块中都可以使用这个变量。使用 let 方法定义变量的一个好处是,它可以记住(memoize)变量的值。(memoize 是个行业术语,不是“memorize”的误拼写。)对上面的代码而言,因为 let 的备忘功能,found_user 的值会被记住,因此不管调用多少次 User 模型测试,find_by 方法只会运行一次。
最终的用户模型为:
class User < ActiveRecord::Base before_save { self.email = email.downcase } VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i validates :name, presence: true, length: { maximum: 50} validates :email, presence:true, format: {with: VALID_EMAIL_REGEX}, uniqueness: {case_sensitive: false} has_secure_password validates :password, length: {minimum: 6} end
require 'spec_helper' describe User do before do @user = User.new(name: "haha", email: "[email protected]", password: "foobar", password_confirmation: "foobar") end subject { @user } it {should respond_to(:name) } it {should respond_to(:email) } it {should respond_to(:password_digest)} it {should respond_to(:password)} it {should respond_to(:password_confirmation)} it {should respond_to(:authenticate)} it {should be_valid} describe "when name is not present" do before {@user.name = " "} it {should_not be_valid} end describe "when email is not present" do before {@user.email = " "} it {should_not be_valid} end describe "when name is too long" do before { @user.name = 'a'*51 } it {should_not be_valid} end describe "when email format is invalid" do it "should be invalid" do addresses = %w[user@foo,com user_at_foo.org example.user@foo. foo@bar_baz.com foo@bar+baz.com] addresses.each do |invalid_address| @user.email = invalid_address expect(@user).not_to be_valid end end end describe "when email format is valid" do it "should be valid" do addresses = %w[[email protected] [email protected] [email protected] [email protected]] addresses.each do |valid_address| @user.email = valid_address expect(@user).to be_valid end end end describe "when email address is already taken" do before do user_with_same_email = @user.dup user_with_same_email.save end it { should_not be_valid } end describe "when password is not present" do before do @user = User.new(name: "Example User", email: "[email protected]", password: " ", password_confirmation: " ") end it { should_not be_valid } end describe "when password doesn't match confirmation" do before { @user.password_confirmation = "mismatch" } it { should_not be_valid } end describe "with a password that's too short" do before { @user.password = @user.password_confirmation = "a" * 5 } it { should be_invalid } end describe "return value of authenticate method" do before { @user.save } let(:found_user) { User.find_by(email: @user.email) } describe "with valid password" do it { should eq found_user.authenticate(@user.password) } end describe "with invalid password" do let(:user_for_invalid_password) { found_user.authenticate("invalid") } it { should_not eq user_for_invalid_password } specify { expect(user_for_invalid_password).to be_false } end end end