Railstutorial:激活用户的集成测试

时间:2015-08-25 18:17:28

标签: ruby-on-rails railstutorial.org

我一直在阅读Railstutorial,并对第二次Chapter 10练习的“额外学分”感到好奇。搜索周围,我遇到了另一个StackOverflow Q&A。我遵循提问者提供的逻辑,所以我也很困惑他们为什么会失败。

我复制了原始提问者的工作&后续失败,但提供的唯一答案并不能解决问题 - 它实际上会产生错误(同样,domain_scope在下一章之前不作为主题进行讨论。)

我会对问题或答案发表评论,但不幸的是,我也没有声誉 - 而且我不想滥用答案功能来分享这个“我也是!”。

对原始提问者充分尊重,这是有问题的代码(从原始问题复制)

users_controller.rb

def index
  @users = User.where(activated: true).paginate(page: params[:page])
end

def show
  @user = User.find(params[:id])
  redirect_to root_url and return unless @user.activated?
end

users_index_test.rb

中的集成测试
test "only show profiles of activated users" do
  log_in_as(@admin)
  get users_path
  assert_template 'users/index'
  assert_select 'div.pagination'
  first_page_of_users = User.paginate(page: 1)
  first_page_of_users.each do |user|
    assert_equal true, user.activated?
  end
end

users.yml

中未激活的用户
non:
  name: Non Activated
  email: nonactivated@example.gov
  password_digest: <%= User.digest('password') %>
  activated: false

后续测试失败

FAIL["test_only_show_profiles_of_activated_users", UsersIndexTest, 2015-08-12 12:45:43 -0700]
test_only_show_profiles_of_activated_users#UsersIndexTest (1439408743.76s)
  Expected: true
    Actual: false
test/integration/users_index_test.rb:34:in `block (2 levels) in <class:UsersIndexTest>'
test/integration/users_index_test.rb:33:in `block in <class:UsersIndexTest>'

'domain-scope'建议在users_index_test.rb

中给出答案
test "only show profiles of activated users" do
  log_in_as(@admin)
  get users_path
  assert_template 'users/index'
  assert_select 'div.pagination'
  first_page_of_users = User.paginate(page: 1)
  first_page_of_users.each do |user|
    default_scope where(:published => true)
  end
end

后续测试错误

ERROR["test_only_show_profiles_of_activated_users", UsersIndexTest, 2015-08-12 12:45:44 -0700]
 test_only_show_profiles_of_activated_users#UsersIndexTest (1439408744.71s)
NoMethodError:         NoMethodError: undefined method `where' for #<UsersIndexTest:0x007faf7aa91d80>
    test/integration/users_index_test.rb:34:in `block (2 levels) in <class:UsersIndexTest>'
    test/integration/users_index_test.rb:33:in `block in <class:UsersIndexTest>'
  test/integration/users_index_test.rb:34:in `block (2 levels) in <class:UsersIndexTest>'
  test/integration/users_index_test.rb:33:in `block in <class:UsersIndexTest>'

我已经尝试了另一种集成测试,但也失败了:

def setup
  @admin          = users(:michael)
  @non_admin      = users(:foo)
  @non_activated  = users(:non)
end

test "only show profiles of activated users" do
  log_in_as(@admin)
  get users_path
  assert_template 'users/index'
  assert_select 'div.pagination'
  first_page_of_users = User.paginate(page: 1)
  first_page_of_users.each do |user|
    assert_not user_path(@non_activated)
  end
end

失败

FAIL["test_only_show_profiles_of_activated_users", UsersIndexTest, 2015-08-12 12:45:43 -0700]
test_only_show_profiles_of_activated_users#UsersIndexTest (1439408743.72s)
  Expected "/users/895943698" to be nil or false
  test/integration/users_index_test.rb:35:in `block (2 levels) in <class:UsersIndexTest>'
  test/integration/users_index_test.rb:34:in `block in <class:UsersIndexTest>’

这些失败似乎表明indexusers_controller.rb操作的代码更改无法正常工作 - 但我已经通过手动测试确认它是。

最值得赞赏的任何帮助!

5 个答案:

答案 0 :(得分:3)

我也在研究这个问题。我想我找到了你的问题。

在测试中,您使用代码

提取用户的第一页
first_page_of_users = User.paginate(page: 1)

当您为非活动用户检查此项时,会出现错误,因为您的Users数据库中的某个条目处于非活动状态。你从来没有把它从你的阵列中删除。

我认为您可以通过使用此行来包含“where activated”条件来解决此问题。

first_page_of_users = User.where(activated: true).paginate(page: 1)

但是我不喜欢这个解决方案,因为它不像你在users_controller中编写的那样检查索引。

这是我的解决方案,我将其包含在我的“index as admin with pagination”test:

User.paginate(page: 1).each do |user|
  if user.activated?
    assert_select 'a[href=?]', user_path(user), text: user.name
    unless user == @admin
      assert_select 'a[href=?]', user_path(user), text: 'delete'
    end
  else
    assert_select 'a[href=?]', user_path(user), text: user.name, count: 0
  end
end

以下是它的工作原理:它会拉出用户的第一页,如果用户被激活,它会查找指向用户的链接以及删除按钮。如果未激活用户,则会检查以确保未显示用户。

答案 1 :(得分:2)

我和Mickey基本上采用了相同的解决方案(并提出了他的解决方案),除了我使用了一个稍微不同的assert_select实现:

User.paginate(page: 1).each do |user|
  if user.activated?
    assert_select 'a[href=?]', user_path(user), text: user.name
    unless user == @admin
      assert_select 'a[href=?]', user_path(user), text: 'delete'
    end
  else
    assert_select 'a[href=?]', user_path(user), false
  end
end

我对assert_select的这种用法的理解是为了确保没有包含未激活用户的user_path的href的锚元素。这基本上与确保具有带有user_path的href的锚元素的数量为零(Mickey的解决方案)相同。测试套件喜欢这两个版本。

这和Mikey的答案适用于额外学分的第一部分,即针对/ users的写入集成测试。对于额外学分的第二部分,即写入集成测试对于 / users /:id ,我执行了以下操作:

首先我做了一个新的集成测试(见脚注为什么):

rails generate integration_test users_show

然后我更新了 app / controllers / users_controller.rb ,并在 test / fixtures / users.yml 中创建了一个未激活的用户。

应用/控制器/ users_controller.rb:

def show
  @user = User.find(params[:id])
  redirect_to root_url and return unless @user.activated?
end

<强>测试/装置/ users.yml里:

.
.
.
archer:
  name: Sterling Archer
  email: duchess@example.gov
  password_digest: <%= User.digest('password') %>
  activated: true
  activated_at: <%= Time.zone.now %>

lana:
  name: Lana Kane
  email: hands@example.gov
  password_digest: <%= User.digest('password') %>
  activated: false

最后,我添加了@unactivated_user和一个新测试,以自动生成 test / integration / users_show_test.rb

require 'test_helper'

class UsersShowTest < ActionDispatch::IntegrationTest
  def setup
    @unactivated_user = users(:lana)
    @activated_user   = users(:archer)
  end

  test "should redirect to root_url when user not activated" do
    log_in_as(@unactivated_user)
    assert_redirected_to root_url
  end

  test "should not redirect root_url when user is activated" do
    log_in_as(@activated_user)
    assert_redirected_to @activated_user
  end
end

执行此操作后,我的测试套件显示为绿色。我添加了@activated_user和第二个测试,以确保第一次测试通过时不是侥幸(因为我在测试中真的很新)。

*脚注:首先我尝试将测试添加到 test / controllers / users_controller_test.rb ,但我很快就明白,虽然这可行,但它不符合它的标准<按照额外信用分配中的规定进行的em>集成测试,似乎需要额外的一步才能使其工作:

  test "should redirect to root_url when user not activated" do
    log_in_as(@unactivated_user)
    get :show, id: @unactivated_user
    assert_redirected_to root_url
  end

在我意识到它可能应该是集成测试而不是控制器测试之后,我找到了一个现有的测试文件来放置它。因为users_edit_test,users_index_test,users_login_test和users_signup_test似乎不适合这个测试,所以我刚刚创建了一个新测试。好像很好。

答案 2 :(得分:0)

对不起安娜,但你得到了&#34;额外信用的第二部分&#34;错

任务是建立一个测试,确保登录用户无法看到未激活的用户

您编写测试的方式(第二次)是未激活的用户将尝试查看自己的个人资料页面 (这也会将他重定向到根网址,但这就是因为每个未激活的用户试图访问的页面都会将他重定向到root_url)

测试应该像那样

require 'test_helper'



class UsersShowTest < ActionDispatch::IntegrationTest
    def setup
   @unactivated_user = users(:moshe)
   @activated_user   = users(:archer)
 end

   test "should only show activated users" do
    log_in_as (@activated_user)
  get user_path(@unactivated_user)
     assert_redirected_to root_url
end
end

答案 3 :(得分:0)

当我尝试将未激活的用户添加到users.yml fixture时,users_index_test.rb测试&#34;索引为admin,包括分页和删除链接&#34;会失败

  

失败[&#34; test_index_as_admin_including_pagination_and_delete_links&#34;,UsersIndexTest,1.7782473900006153]    test_index_as_admin_including_pagination_and_delete_links #UsersIndexTest(1.78s)           预计至少有1个元素匹配&#34; a [href =&#34; / users / 659682706&#34;]&#34;,找到0 ..           预期0为&gt; = 1。           test / integration / users_index_test.rb:18:in block (2 levels) in <class:UsersIndexTest>' test/integration/users_index_test.rb:17:in块中&#39;

我首先尝试通过添加

简单地在该测试中包含激活用户的测试
if user == @unactivated_user
assert_redirected_to root_url
else
<the original test code>

那产生了错误

  

失败[&#34; test_index_as_admin_including_pagination_and_delete_links&#34;,UsersIndexTest,1.7827676309971139]    test_index_as_admin_including_pagination_and_delete_links #UsersIndexTest(1.78s)           预期的响应是&lt; 3XX:重定向&gt;,但是&lt; 200:OK&gt;           test / integration / users_index_test.rb:19:在block (2 levels) in <class:UsersIndexTest>' test/integration/users_index_test.rb:17:in块中&#39;

所以我将if条件更改为true,并添加了一个以非管理员激活的用户身份登录的单独测试。这似乎有效。我的users_index_test.rb已完整:

require 'test_helper'

class UsersIndexTest < ActionDispatch::IntegrationTest

  def setup
    @admin = users(:michael)
    @non_admin = users(:archer)
    @unactivated_user = users(:malory)
  end

  test "index as admin including pagination and delete links" do
    log_in_as(@admin)
    get users_path
    assert_template 'users/index'
    assert_select 'div.pagination', count: 2
    first_page_of_users = User.paginate(page: 1)
    first_page_of_users.each do |user|
      if user == @unactivated_user
        true
      else
        assert_select 'a[href=?]', user_path(user), text: user.name
          unless user == @admin
            assert_select 'a[href=?]', user_path(user), text: 'delete'
          end
      end
    end
    assert_difference 'User.count', -1 do
      delete user_path(@non_admin)
    end
  end
  #
  #   User.paginate(page:1).each do |user|
  #     assert_select 'a[href=?]', user_path(user), text: user.name
  #   end
  # end
  test "index as non-admin" do
    log_in_as(@non_admin)
    get users_path
    assert_select 'a', text: 'delete', count: 0
  end

  test "should only show activated users" do
    log_in_as(@non_admin)
    get user_path(@unactivated_user)
    assert_redirected_to root_url
  end

end

我的users.yml:

# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
#empty
michael:
  name: Michael Example
  email: michael@example.com
  password_digest: <%= User.digest('password') %>
  admin: true
  activated: true
  activated_at: <%= Time.zone.now %>

archer:
  name: Sterlin Archer
  email: duchess@example.gov
  password_digest: <%= User.digest('password') %>
  activated: true
  activated_at: <%= Time.zone.now %>

lana:
  name: Lana Kane
  email:  hands@example.gov
  password_digest: <%= User.digest('password') %>
  activated: true
  activated_at: <%= Time.zone.now %>

malory:
  name: Malory Archer
  email:  boss@example.gov
  password_digest: <%= User.digest('password') %>
  activated: false
  #activated_at: <%= Time.zone.now %>

<% 30.times do |n| %>
user_<%= n %>:
  name: <%= "User #{n}" %>
  email: <%= "user-#{n}@example.com" %>
  password_digest: <%= User.digest('password') %>
  activated: true
  activated_at: <%= Time.zone.now %>
  <% end %>

我的users_controller.rb:

class UsersController < ApplicationController
  before_action :logged_in_user, only: [:index, :edit, :update, :destroy]
  before_action :correct_user, only: [:edit, :update]
  before_action :admin_user, only: :destroy

  def index
    @users = User.where(activated: true).paginate(page: params[:page])
  end

  def show
    @user = User.find(params[:id])
    redirect_to root_url and return unless @user.activated?
  end

  def new
    @user = User.new
  end

  def create
    @user = User.new(user_params)
    if @user.save
      @user.send_activation_email
      flash[:info] = "Please check your email to activate your account."
      redirect_to root_url
    else
      render 'new'
    end
  end

  def edit
    @user= User.find(params[:id])
  end

  def update
    #@user = User.find(params[:id])
    if @user.update_attributes(user_params)
      #sign_in @user
      flash[:success] = "Profile updated"
      redirect_to @user
    else
      render 'edit'
    end
  end

  def destroy
    User.find(params[:id]).destroy
    flash[:success] = "User deleted"
    redirect_to users_path
  end

  private
  def user_params
    params.require(:user).permit(:name, :email, :password, :password_confirmation)
  end

  # Before filters

  # Confirms a logged-in user
  def logged_in_user
    unless logged_in?
      store_location
      flash[:danger] = "Please log in."
      redirect_to login_url
    end
  end

  # Confirms the correct user.
  def correct_user
    @user = User.find(params[:id])
    redirect_to(root_url) unless current_user?(@user)
  end

  #Confirms an admin user
  def admin_user
    redirect_to(root_url) unless current_user.try(:admin?)
  end



end

希望我的解决方案可以帮助某人。

答案 4 :(得分:0)

此集成测试也存在问题。 不要忘记在灯具中设置至少1个用户&#34;激活= false&#34; 我的简短解决方案,希望它可以帮到某人 的 users.yml里

purrr

仅检查&#34;未激活的用户未显示&#34;

<强> users_index_test.rb

michael:
    name: Michael Example
    email: michael@example.com
    password_digest: <%= User.digest('password') %>
    admin: true
    activated: true
    activated_at: <%= Time.zone.now %>


  archer:
    name: Sterling Archer
    email: duchess@example.gov
    password_digest: <%= User.digest('password') %>
    activated: false #do not forget to set at least 1 user activated to false
    activated_at: <%= Time.zone.now %>

  lana:
    name: Lana Kane
    email: hands@example.gov
    password_digest: <%= User.digest('password') %>
    activated: true
    activated_at: <%= Time.zone.now %>

  malory:
    name: Malory Archer
    email: boss@example.gov
    password_digest: <%= User.digest('password') %>
    activated: true
    activated_at: <%= Time.zone.now %>

  <% 30.times do |n| %>
  user_<%= n %>:
    name:  <%= "User #{n}" %>
    email: <%= "user-#{n}@example.com" %>
    password_digest: <%= User.digest('password') %>
    activated: true
    activated_at: <%= Time.zone.now %>
  <% end %>

请勿忘记更改 users_controller.rb 以确保测试正确性

class UsersIndexTest < ActionDispatch::IntegrationTest
    def setup
      @admin     = users(:michael)
      @non_admin = users(:lana)
    end
    test 'display only activated users' do
      log_in_as(@non_admin)
      get users_path
      first_page_of_users = User.where(activated: false).paginate(page: 1)
      first_page_of_users.each do |user|
        assert_select 'a[href=?]', user_path(user), text: user.name, count: 0
      end
    end

  end

这样你就可以检查测试是否正常工作

ADD 不会将最旧的测试索引作为管理员包括分页和删除链接 添加 where(activated = true),如下所示

    def index 
      # @users = User.where(activated: true).paginate(page: params[:page])        
      @users = User.paginate(page: params[:page])
    end