【问题标题】:How to create DRYer Rspec with Factory Girl如何使用 Factory Girl 创建 DRYer Rspec
【发布时间】:2015-05-11 08:10:07
【问题描述】:

我正在构建一个简单的博客应用程序,以便使用 RSpec 和 Factory Girl 来学习 BDD/TDD。在这个过程中,我继续遇到“失败”,但我相信他们更多地与我如何使用工厂女孩有关。

正如您将在下面看到的那样,为了让我的规格通过,我很难保持我的测试 DRY - 我一定有什么误解。你会注意到,我没有充分利用 Factory Girl 的潜力,有时甚至完全跳过它。我发现在规范中使用 get :createget :showput :update 等函数时,我通常会遇到问题。

我目前停留在 #PUT update 规范上,该规范应该只是测试 @post 变量的分配。我已经尝试了多种我在网上找到的这种规格,但似乎没有一种工作 - 因此,它是工厂女孩吗?也许我在网上找到的规格是过时的 Rspec 版本?

我正在使用: 规范 3.1.7 导轨 4.1.6

posts_controller_spec.rb

require 'rails_helper'
require 'shoulda-matchers'

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


    describe "#GET index" do
            it 'renders the index template' do
                get :index
                expect(response).to be_success
            end

            it "assigns all posts as @posts" do
                post = Post.create(title: 'Charlie boy', body: 'Bow wow wow ruff')
                get :index
                expect(assigns(:posts)).to eq([post])
            end
    end


    describe '#GET show' do
            it 'assigns the request post to @post' do
                post = Post.create!(title: 'Charlie boy', body: 'Bow wow wow ruff')
                get :show, id: post.id
                expect(assigns(:post)).to eq(post)
            end
    end



    describe '#GET create' do
        context 'with valid attributes' do
          before :each do
            post :create, post: attributes_for(:post)
          end

              it 'creates the post' do
                expect(Post.count).to eq(1)
                expect(flash[:notice]).to eq('Your post has been saved!')
              end

              it 'assigns a newly created post as @post' do
                expect(assigns(:post)).to be_a(Post)
                expect(assigns(:post)).to be_persisted
              end

              it 'redirects to the "show" action for the new post' do
                expect(response).to redirect_to Post.first
              end
        end


        context 'with invalid attributes' do
            before :each do
                post :create, post: attributes_for(:post, title: 'ha')
            end

                it 'fails to create a post' do
                    expect(Post.count).to_not eq(1)
                    expect(flash[:notice]).to eq('There was an error saving your post.')
                end

                it 'redirects to the "new" action' do
                    expect(response).to redirect_to new_post_path
                end
        end
    end



    describe '#GET edit' do
            it 'assigns the request post to @post' do
                post = Post.create!(title: 'Charlie boy', body: 'Bow wow wow ruff')
                get :edit, id: post.id
                expect(assigns(:post)).to eq(post)
            end
    end

    describe '#PUT update' do
        context 'with success' do
          before :each do
            post :create, post: attributes_for(:post)
          end

            it 'assigns the post to @post' do
                put :update, id: post.id
                expect(assigns(:post)).to eq(post)
            end
        end
    end

end

posts_controller.rb

class PostsController < ApplicationController

    def index
        @posts = Post.all.order('created_at DESC')
    end


    def new
        @post = Post.new
    end


    def create
        @post = Post.new(post_params)

        if @post.save
            flash[:notice] = "Your post has been saved!"
            redirect_to @post
        else
            flash[:notice] = "There was an error saving your post."
            redirect_to new_post_path
        end
    end


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


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


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

        # if @post.update(params[:post].permit(:title, :body))
        #   flash[:notice] = "Your post is updated!"
        #   redirect_to @post
        # else
        #   flash[:notice] = "There was an error updating your post."
        #   render :edit
        # end
    end



    private

    def post_params
        params.require(:post).permit(:title, :body)
    end
end

factories/post.rb

FactoryGirl.define do
    factory :post do
        title 'First title ever'
        body 'Forage paleo aesthetic food truck. Bespoke gastropub pork belly, tattooed readymade chambray keffiyeh Truffaut ennui trust fund you probably haven\'t heard of them tousled.'
    end
end

当前故障:

Failures:

  1) PostsController#PUT update with success assigns the post to @post
     Failure/Error: put :update, id: post.id
     ArgumentError:
       wrong number of arguments (0 for 1+)
     # ./spec/controllers/posts_controller_spec.rb:86:in `block (4 levels) in <top (required)>'

Finished in 0.19137 seconds (files took 1.17 seconds to load)
17 examples, 1 failure

【问题讨论】:

    标签: ruby-on-rails ruby rspec factory-bot bdd


    【解决方案1】:

    您的规范失败的直接原因是您每次测试只能调用一次控制器,而对于更新,您调用它两次:在操作前,您调用 create... 然后在您称为更新的更新测试的主要部分......控制器规格不喜欢那样。

    为了使现有规范正常工作,您需要将操作前的 post :create, post: attributes_for(:post) 行替换为仅创建一个帖子或(如前所述)使用工厂女孩创建一个帖子 - 而不是尝试通过调用控制器来完成它。

    【讨论】:

    • 这是一些很棒的信息。我一定会记住这一点。谢谢塔林!
    【解决方案2】:

    你绝对可以在这里利用工厂。

    你创建的工厂其实也不错。

    而不是这样做: post = Post.create(title: 'Charlie boy', body: 'Bow wow wow ruff')

    这样做:post = FactoryGirl.create(:post)

    如果你这样做,你可以得到更多的干燥:

    # in spec/rails_helper.rb
    RSpec.configure do |config|
      config.include FactoryGirl::Syntax::Methods
    end
    

    这将允许您在规范中执行此操作:post = create(:post)

    关于你的 PUT 测试,试试这个from a previous SO answer

    describe '#PUT update' do
      let(:attr) do 
        { :title => 'new title', :content => 'new content' }
      end
    
      context 'with success' do
        before :each do
          @post = FactoryGirl.create(:post)
        end
    
        it 'assigns the post to @post' do
          put :update, :id => @post.id, :post => attr
          @post.reload
          expect(assigns(:post)).to eq(post)
        end
      end
    end
    

    编辑:

    另外,如果需要,不要害怕将东西移至before :each do。他们非常擅长保持干燥

    【讨论】:

    • 很高兴再次收到您的来信!在我们说话的时候,我正在整理规格。我还尝试了您在上面提供的 PUT 的答案,并收到了一个错误,我在其他测试之前实际上遇到了很多错误。我会把它添加到下面的评论中..
    • Failure/Error: expect(assigns(:post)).to eq(post) expected: {:title=&gt;"new title", :content=&gt;"new content"} got: #&lt;Post id: 1, title: "First title ever", body: "Forage paleo aesthetic food truck. Bespoke gastrop...", created_at: "2015-03-10 03:26:38", updated_at: "2015-03-10 03:26:38"&gt; (compared using ==) Diff: @@ -1,3 +1,2 @@ -:content =&gt; "new content", -:title =&gt; "new title",
    • 嗯,您的控制器中的更新操作已被注释掉,并且没有实际的更新发生。所以你可能想改变它,让它真正更新
    • 我认为我需要首先测试最初的代码行,它只是将请求的帖子分配给实例变量@post。当取消注释更新操作的其余部分时,我仍然收到以下错误.. NoMethodError: undefined method 'id' ..or.. `失败/错误:put :update, :id => @post.id, :post => @post NoMethodError : 未定义的方法permit' for "1":String
    • 把它改成@post.update(post_params)而不是@post.update(params[:post].permit(:title, :body))
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-01-14
    • 1970-01-01
    • 1970-01-01
    • 2014-01-12
    • 1970-01-01
    相关资源
    最近更新 更多