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
- 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) }