RSpec Ajax 点赞按钮测试未通过

RSpec Ajax Test for Like Button Not Passing

我有一个 RSpec 控制器规格,根本不会通过。我正在尝试测试一个点赞按钮,它是一个 AJAX post 请求,它会更新 post 下显示页面上的点赞数量。当我在 localhost 上进行物理测试时,我可以单击 post 下的赞按钮,并且赞的数量按预期增加 1。当我 运行 我的控制器规格时,它一直失败:

PostsController POST #vote a user can vote on a post increments the number of votes

Failure/Error: expect { post1.votes }.to change{post1.votes}.by(1)
           expected result to have changed by 1, but was changed by 0
         # ./spec/controllers/posts_controller_spec.rb:17:in `block (4 levels) in <top (required)>'

以上来源于此:

/spec/controllers/posts_controller_spec.rb

require 'rails_helper'
require 'capybara/rails'

RSpec.describe PostsController, :type => :controller do

  describe "POST #vote" do

    let(:post1) {Post.create!(content: "The Content created", votes: 1, story: "My story", author: "Authors Name")}

    context "a user can vote on a post" do
      it "increments the number of votes" do

        post :vote, post1: { votes: post1.votes }, 
          params: { id: post1.id },
          xhr: true

        expect { post1.votes }.to change{post1.votes}.by(1)

      end
    end
  end
end

我尝试了 expect { post1.votes }.to change{post1.votes}.by(1) 的一些变体,包括但不限于:expect { post1.votes }.to change{post1.votes}.from(1).to(2),但仍然没有通过。我不确定我哪里出了问题。我还制作了一个功能规范,内容如下:

/spec/features/liking_spec.rb

require 'rails_helper'

RSpec.feature "Like feature" do

  let!(:post) {Post.create!(content: "The Content created", votes: 1, story: "My story", author: "Authors Name")}

  scenario "users can like quotes" do
    visit root_path

    click_link "Motivation"
    click_link post.author

    expect(page).to have_button "Like"
    expect(page).to have_current_path post_path(post.id)

    click_button "Like"

    expect { post.votes }.to change{post.votes}.by(1)

  end
end

这也失败并显示与上面相同的消息。

这是我的 AJAX 点击 "like" 请求,点赞数增加 1。

app/assets/javascripts/like.js

"use strict";

$(document).ready(function(){

  // On click increments likes by 1. 
  $("#liking").click(function() {
    console.log('clicked');
    var id = $(this).data("id");
    $.ajax({
        url: '/posts/vote',
        type: 'POST',
        data: {
          id: id,
          increment: true
        },
        success: function() {
          var like_counter = $('#likeit');
          var current_count = parseInt($('#likeit').html());
          like_counter.html(current_count + 1); 
          $("#liking").hide();
        }
    });
  });
});

在我的 post 控制器中:

app/controllers/posts_controller.rb

class PostsController < ApplicationController

  def index
    @posts = Post.all
  end

  def show
    @post = Post.find(params[:id])
  end

  def vote
    @post = Post.find(params[:id])
    @post.update(votes: @post.votes + 1)
  end
end

最后,这是我的 show.html.erb 点赞按钮所在的位置。

<center>
  <strong>Likes:</strong>
</center>
<center>
  <p id='likeit'><%= @post.votes %></p>
</center>
<center>
  <button id="liking" type="submit" data-id="<%= @post.id %>">Like</button>
</center>

如果您需要更多信息,请告诉我。

您没有正确使用 expect {}.to change ...

本来就是expect { the action that causes the change }.to change { the value that will change }.by(1)

在您的控制器规格示例中,类似于

expect do
  post :vote, post1: { votes: post1.votes }, 
     params: { id: post1.id },
     xhr: true
end.to change { post1.reload.votes }.by(1)

请注意 post1 上的 reload 调用,即当匹配器 re-evaluates 执行 post 后对象得到更新的投票值时。

您遇到的第二个问题是在您的功能规范中。即使您更新它以正确使用更改匹配器 (expect { click_button "Like"}.to change...),您的测试很可能仍然会失败,因为在 click_button "Like" returns 时投票计数实际上并未更新。这是因为 click_button 异步触发行为然后 returns。您真的不应该在功能测试中测试数据库值更新,而应该检查页面上发生变化的可见值,但是如果您坚持在那里检查数据库值,您将需要使用某种类型的等待匹配器(https://github.com/abotalov/waiting_rspec_matchers) 或点击按钮后休眠。