/ unit test

Rails 单元测试——用fixture和helper精简你的代码

Y87zIwv-4

在编写登录用户部分逻辑的测试用例时容易写出冗余的代码,有代码洁癖的人难以忍受。

举个栗子

用户密码生成使用bcrypt,数据库中实际上不存password,只存password_digest
test/fixtures/users.yml:

finn:
  username: finnthehuman
  email: finn@adventuretime.com
  password_digest: <%= BCrypt::Password.create("pbpbpbpb") %> # fixture支持erb语法

在测试需要登录的controller时,需要写出这样的代码

...
post login_path, params: {username: users(:finn).username, password: "pbpbpbpb"}
...

有代码洁癖的你看到hard code的代码(password: "pbpbpbpb")非常不自在,还好在rails提供了一种解决方案。

解决方案

test/test_helper.rb

...
module PasswordHelper
  def default_password
    "default_password"
  end
  def password_digest
    BCrypt::Password.create(default_password) 
  end
end
...

class ActiveSupport::TestCase
  include PasswordHelper
  ActiveRecord::FixtureSet.context_class.send :include, PasswordHelper
end

test/fixtures/users.yml:

finn:
  username: finnthehuman
  email: finn@adventuretime.com
  password_digest: <%= BCrypt::Password.create(default_password) %> # fixture支持erb语法

测试用例就可以使用default_password

...
post login_path, params: {username: users(:finn).username, password: default_password}
...

然后你发现每次在测试需要登录的功能时都要写这么长的post login_path, params: {username: users(:finn).username, password: default_password},你也可以把它抽出来,写在helper里。

test/test_helper.rb

module LoginHelper
  def login_in_as(user)
    post "/login", params: {username: user.email, password: default_password}
  end
end

class ActiveSupport::TestCase
  ...
  include LoginHelper
  ...
end

然后只要在你的单元测试代码中使用login_in_as users(:finn)就可以了。