Home > Software engineering >  FactoryBot presenting unexpected behaviour when testing an instance variable assignment in a control
FactoryBot presenting unexpected behaviour when testing an instance variable assignment in a control

Time:12-02

I'm writing controller tests with Rspec in a Rails 7 application and getting some unexpected behaviour when asserting against an assignment.

The controller action that I'm testing returns the instance variable @prefectures;

  def edit
    @prefectures = Prefecture.all.order(:code)
    @city = City.find(params[:id])

    respond_to do |format|
      format.html { render :edit, locals: { city: @city } }
    end
  end

In the tests I'm using FactoryBot to create a list of three Prefectures and then checking in the example that the assigned @prefectures matches those three.

RSpec.describe 'Cities', type: :request do
  let(:city) { create(:city) }
  let(:prefecture_list) { create_list(:prefecture, 3) }

  describe 'GET /edit' do
    it 'succeeds' do
      get edit_city_path(city)
      expect(response).to be_successful
      expect(response.status).to eq(200)
    end

    it 'assigns @prefectures' do
      expected = prefecture_list.sort_by { |prefecture| prefecture.code }
      get edit_city_path(city)
      expect(assigns(:prefectures)).to eq(expected)
    end
  end
end

There are several actions on the controller that return an instance variable of @prefectures. There are also a number of tests in the test file that assert the assignment of @prefectures using the same FactoryBot list. In each instance FactoryBot instantiates a collection of three prefectures and the test confirms that the assigned '@prefectures' is that same collection of three Prefectures.

In the 'GET /edit' tests, however, the test fails because for reasons I don't understand, the assigned @prefectures is a collection of 6 Prefectures rather than 3. Each time it runs it's a distinct collection of six so it's not as if three are somehow being saved between test runs and added to. It seems as though in this test instance FactoryBot is creating a list of 6 rather than three.

Any ideas on why this is happening?

CodePudding user response:

You need to clean the database after each suite

Gem database_cleaner

  • Example config:
RSpec.configure do |config|

  config.before(:suite) do
    DatabaseCleaner.strategy = :transaction
    DatabaseCleaner.clean_with(:truncation)
  end

  config.around(:each) do |example|
    DatabaseCleaner.cleaning do
      example.run
    end
  end

end

CodePudding user response:

I think I figured it out. In the test setup I also create a city_list;

let(:city_list) { create_list(:city, 3) }
let(:prefecture_list) { create_list(:prefecture, 3) }

The Prefecture factory creates a Prefecture;

FactoryBot.define do
  factory :prefecture do
    name { Faker::Address.state }
    code { Faker::Number.number(digits: 3) }
  end
end

The City factory creates a City with a Prefecture because there is a one-to-many relationship between the two;

FactoryBot.define do
  factory :city do
    prefecture { create(:prefecture) }
    name { Faker::Address.city }
    rating { Faker::Number.within(range: 0.0..5.0) }
  end
end

The test that is failing is unique among the tests that assert the @prefectures assignment as it is the only one that also uses city_list. city_list appears to create an additional set of three 'Prefectures`.

I fixed the issue by overriding the prefecture_id constraint of the city_list to use one of the existing prefectures rather that generate it's own new one and the test now passes.

let(:prefecture_list) { create_list(:prefecture, 3) }
let(:city_list) { create_list(:city, 3, prefecture_id:prefecture_list[0].id) }
  • Related