user1185081
user1185081

Reputation: 2118

How to test child creation in hierarchical parent-child relationship with Rspec / Rails 5.2?

My application manages BusinessFlows, which can only be created within a parent BusinessArea. The 'new' method in BusinessFlows controller is:

  def new
    @business_area = BusinessArea.find(params[:business_area_id])
    @business_flow = BusinessFlow.new
  end

The Factory used for testing works fine, and creates the parent BusinessArea, and the the BusinessFlow as expected:

FactoryBot.define do
  factory :business_flow do
    association :parent,  factory: :business_area
    playground_id       {0}
    name                {"Test Business Flow"}
    code                {Faker::Code.unique.rut}
    description         {"This is a test Business Flow used for unit testing"}
    created_by          {"Fred"}
    updated_by          {"Fred"}
    owner_id            {0}
    responsible_id      {0}
    deputy_id           {0}
    organisation_id     {0}
    status_id           {0}
  end
end

But when it comes to test if the 'new' view can be displayed, the controller raises an error due to missing params[:business_area_id]:

 ActiveRecord::RecordNotFound:
   Couldn't find BusinessArea without an ID

Here is the feature test script:

require 'rails_helper'

RSpec.describe BusinessFlow, type: :request do
  include Warden::Test::Helpers

  describe "Business Flows pages: " do
    let(:bf) {FactoryBot.create(:business_flow)}

    context "when not signed in " do
      it "should propose to log in when requesting index view" do
        get business_flows_path
        follow_redirect!
        expect(response.body).to include('Sign in')
      end
      it "should propose to log in when requesting new view" do
        get new_business_flow_path
        follow_redirect!
        expect(response.body).to include('Sign in')
      end
      it "should propose to log in when requesting edit view" do
        get edit_business_flow_path(bf)
        follow_redirect!
        expect(response.body).to include('Sign in')
      end
      it "should propose to log in when requesting show view" do
        get business_flow_path(bf)
        follow_redirect!
        expect(response.body).to include('Sign in')
      end
    end
    context "when signed in" do
      before do
        get "/users/sign_in"
        test_user = FactoryBot.create(:user)
        login_as test_user, scope: :user
      end
      it "should display index" do
        get business_flows_path
        expect(response).to render_template(:index)
      end
      it "should display new view" do
        get new_business_flow_path(bf.parent.id)
        expect(response).to render_template(:_form)
      end
      it "should display edit view" do
        get edit_business_flow_path(bf)
        expect(response).to render_template(:_form)
      end
      it "should display show view" do
        get business_flow_path(bf)
        expect(response).to render_template(:show)
      end
    end
  end
end

Only the 'new' method test in the context 'when signed in' fails. Can you please help me solving this?

Thanks a lot!

Upvotes: 0

Views: 338

Answers (2)

user1185081
user1185081

Reputation: 2118

As noticed by Max, in a parent-child relationship, using the nested route it the right way. It avoids hardcoding the parameter expected by the 'new' method of the controller.

The complete solution would be the following:

 require 'rails_helper'

RSpec.describe BusinessFlow, type: :request do
  include Warden::Test::Helpers

  describe "Business Flows pages: " do
    let(:ba) {FactoryBot.create(:business_area)}
    let(:bf) {FactoryBot.create(:business_flow)}

    context "when signed in" do
      before do
        get "/users/sign_in"
        test_user = FactoryBot.create(:user, is_admin: true)
        login_as test_user, scope: :user
      end
      it "should display index" do
        get business_flows_path
        expect(response).to render_template(:index)
      end
      it "should display new view" do
        get new_business_area_business_flow_path(ba)
        expect(response).to render_template(:_form)
      end
      it "should display edit view" do
        get edit_business_flow_path(bf)
        expect(response).to render_template(:_form)
      end
      it "should display show view" do
        get business_flow_path(bf)
        expect(response).to render_template(:show)
      end
    end
  end
end

Where the let(:ba) {FactoryBot.create(:business_area)} statement provides the business area from which to create the child business flow.

Note the nested route get new_business_area_business_flow_path(ba) based on the business area - ba - instance.

Upvotes: 0

Joel Blum
Joel Blum

Reputation: 7888

 it "should display new view" do
     get new_business_flow_path(business_area_id: bf.parent)
     expect(response).to render_template(:_form)
 end

Rails thinks the id you're passing is part of the business flow path (and it isn't), it needs to be passed as a param I think.

Upvotes: 0

Related Questions