RSpec报错:期望#count变更1次但实际变更0次,求排查方法
Hey Jake, let's walk through the most likely culprits here—this is a super common gotcha when writing controller specs, so we'll nail it down step by step.
1. Did your controller actually save the record?
The easiest mistake to make is forgetting to persist the Location to the database in your create action. I’ve lost count of how many times I’ve seen code like this:
def create @location = Location.new(location_params) if @location.valid? render json: @location, status: :created else render json: @location.errors, status: :unprocessable_entity end end
See the problem? You’re checking validity, but never calling @location.save! The record stays in memory, so your count change expectation fails even if validation passes. Fix this by swapping valid? for save:
def create @location = Location.new(location_params) if @location.save render json: @location, status: :created else render json: @location.errors, status: :unprocessable_entity end end
2. Are your strong parameters permitting required fields?
If you’re not allowing region_id (the attribute linking Location to Region) in your location_params method, the value from your factory will get stripped out. That triggers the "region must exist" validation, so the save fails and your count stays at 0.
Check your params method—if it looks like this:
def location_params params.require(:location).permit(:name) # Missing region_id! end
Update it to include the required attribute:
def location_params params.require(:location).permit(:name, :region_id) end
3. Is your test environment missing valid Region data?
Even if you loaded seed data, RSpec doesn’t automatically load seeds into the test environment unless you explicitly configure it to. If your test database has no Region records, your Location’s region_id will point to a non-existent entry, causing validation to fail.
Instead of relying on seeds, use one of these more reliable approaches:
- Create a Region directly in your spec with
let!(:region) { create(:region) } - Update your Location factory to auto-associate a Region:
FactoryBot.define do factory :location do name { "Downtown" } region { association(:region) } # Creates a new Region if none exists end end
4. Debug the test directly to see what’s failing
Add debug output to your spec to uncover hidden errors:
it "creates a new Location" do post :create, params: { location: attributes_for(:location) } # Add these lines to diagnose the issue puts response.body # Shows validation errors if any puts Location.last&.inspect # Checks if a record was saved puts Location.count # Confirms the current database count expect(Location.count).to have_changed_by(1) end
Running this will tell you exactly why the count isn’t changing—whether it’s a validation error, missing params, or something else.
5. Double-check your request method and param structure
Make sure you’re using post :create (not get or another method) and that your params are wrapped under the :location key (matching what your controller expects).
Let me know which of these fixes your issue—odds are it’s one of the first two!
内容的提问来源于stack exchange,提问作者Jake

