You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Rails 6 API RSpec请求测试冲突问题求助

解决React-Rails中RSpec接口测试与收藏删除功能冲突的问题

我之前也碰到过类似的坑,核心就是测试环境里没把登录用户和测试数据正确关联起来,导致控制器里的current_user.favourite_cocktails.find_by找不到记录,返回nil后调用delete直接报错。下面是一套能让功能和测试都正常跑的完整方案,包含所有需要调整的代码:

一、问题根源先理清楚

  1. 最初直接用FavouriteCocktail.find(params[:id])删除的逻辑,测试能过但实际功能失效——因为这个逻辑没做用户校验,随便哪个用户都能删别人的收藏,而且前端大概率传的是鸡尾酒ID而非收藏记录ID,实际根本找不到对应记录。
  2. 重构后的current_user.favourite_cocktails.find_by(cocktail_id: params[:id])才是符合业务逻辑的(只删当前用户的对应收藏),但测试里FactoryBot创建的收藏没绑定到测试用的登录用户,所以current_user.favourite_cocktails是空的,find_by返回nil就触发了NoMethodError。

二、完整代码修改方案

1. Gemfile(确保测试依赖齐全)

gem 'rails', '~> 7.0'
gem 'react-rails'
# 测试相关依赖
group :test do
  gem 'rspec-rails'
  gem 'factory_bot_rails'
  gem 'devise' # 假设你用devise做用户认证
  gem 'shoulda-matchers'
end

2. 路由配置(config/routes.rb)

保证路由符合RESTful风格,嵌套在api/v1下:

Rails.application.routes.draw do
  namespace :api do
    namespace :v1 do
      resources :favourite_cocktails, only: [:destroy]
      # 其他业务路由...
    end
  end
end

3. 控制器逻辑(app/controllers/api/v1/favourite_cocktails_controller.rb)

保留正确的业务逻辑,同时加错误处理避免nil调用:

class Api::V1::FavouriteCocktailsController < ApplicationController
  before_action :authenticate_user! # 强制用户登录才能操作

  def destroy
    @favourite_cocktail = current_user.favourite_cocktails.find_by(cocktail_id: params[:id])
    if @favourite_cocktail
      @favourite_cocktail.destroy
      head :no_content
    else
      render json: { error: '收藏记录不存在' }, status: :not_found
    end
  end
end

4. FactoryBot配置(spec/factories/favourite_cocktails.rb)

修改Factory,让它可以绑定到指定用户,默认也会自动关联一个用户:

FactoryBot.define do
  factory :favourite_cocktail do
    user { association :user } # 默认创建并关联一个新用户
    cocktail { association :cocktail } # 假设你有Cocktail模型
  end
end

5. 模型约束(app/models/favourite_cocktail.rb)

确保模型有正确的关联和唯一性校验,避免重复收藏:

class FavouriteCocktail < ApplicationRecord
  belongs_to :user
  belongs_to :cocktail

  validates :user_id, uniqueness: { scope: :cocktail_id, message: '你已经收藏过这款鸡尾酒啦' }
end

6. RSpec测试用例(spec/controllers/api/v1/favourite_cocktails_controller_spec.rb)

核心是登录测试用户,并创建和该用户绑定的收藏记录:

require 'rails_helper'

RSpec.describe Api::V1::FavouriteCocktailsController, type: :controller do
  let(:test_user) { create(:user) } # 创建测试用用户
  let(:test_cocktail) { create(:cocktail) }
  # 创建绑定到测试用户的收藏记录
  let(:user_favourite) { create(:favourite_cocktail, user: test_user, cocktail: test_cocktail) }

  before do
    sign_in test_user # 登录测试用户,需要devise的测试helper支持
  end

  describe 'DELETE #destroy' do
    context '当用户确实收藏过这款鸡尾酒时' do
      it '成功删除收藏记录并返回204' do
        user_favourite # 提前创建收藏记录
        expect {
          delete :destroy, params: { id: test_cocktail.id } # 传鸡尾酒ID,匹配控制器逻辑
        }.to change(FavouriteCocktail, :count).by(-1)

        expect(response).to have_http_status(:no_content)
      end
    end

    context '当用户没收藏过这款鸡尾酒时' do
      it '返回404错误提示' do
        delete :destroy, params: { id: 9999 } # 传一个不存在的鸡尾酒ID

        expect(response).to have_http_status(:not_found)
        expect(JSON.parse(response.body)['error']).to eq('收藏记录不存在')
      end
    end
  end
end

7. 配置RSpec支持Devise(spec/rails_helper.rb)

在rails_helper里添加Devise的测试helper,让sign_in方法能正常用:

RSpec.configure do |config|
  # 其他配置项...
  config.include Devise::Test::ControllerHelpers, type: :controller
end

三、验证步骤

  1. 跑测试:bundle exec rspec spec/controllers/api/v1/favourite_cocktails_controller_spec.rb,所有用例应该都能通过;
  2. 实际应用测试:登录用户,收藏一款鸡尾酒后点击删除,确认功能正常,且不会影响其他用户的收藏记录。

这样调整后,既保证了业务逻辑的正确性(只能删除自己的收藏),又让测试能正确关联用户和数据,彻底解决冲突问题。

内容的提问来源于stack exchange,提问作者Tunde

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.07 10:07:40