RSpec测试Active Campaign报Invalid Record User Must exist错误
问题原因
测试失败由两个直接问题导致,附带一个隐藏的控制器代码bug:
- 测试参数硬编码
app_user_id: 12,但测试环境使用独立的事务性测试数据库,默认每次运行测试会清空数据。你定义的let(:user) { create(:user) }是懒加载逻辑,整个测试用例中从未引用过user变量,因此这个测试用户根本不会被创建;就算引用了,FactoryBot生成的第一条用户id为1,和你硬编码的12完全不匹配,Token模型校验关联用户存在性时找不到对应id的用户,自然返回user must exist的422错误。 - 断言逻辑完全错误:你对响应体执行
JSON.dump得到的是序列化后的字符串,直接和整数200做相等比对永远不可能通过,你需要比对的是HTTP响应状态码,或者解析响应体后校验业务返回字段。 - 控制器存在隐藏变量名笔误:
update_contact方法中先赋值contact = create_token,后续构造请求参数时却写了token.email_token,token是未定义的局部变量,触发到对应分支会直接抛NameError,非测试环境能跑通只是因为你没覆盖到这个执行路径。
修复方案
- 调整测试用例的用户定义和参数逻辑,不要硬编码用户id,确保测试用户提前创建:
- 把
let改成let!,强制在执行测试用例前就创建测试用户,避免懒加载不生效的问题 - 参数中的
app_user_id使用实际创建的测试用户id,不要写死12
- 把
- 修正断言逻辑,正确校验HTTP状态码和响应体内容
- 修正控制器里的变量名笔误,把
token.email_token改成contact.email_token
修正后的测试代码:
require 'rails_helper' RSpec.describe 'Create Token for Active Campaign', type: :request do # let! 强制提前创建记录,避免懒加载不执行 let!(:user) { create(:user) } let(:create_active_params) do { contact: { id: '100', email: 'john@sample.com', app_user_id: user.id, # 关联实际创建的测试用户 first_name: 'john', last_name: 'Doe', tags: 'form-step-1' } } end it 'updates a contact' do VCR.use_cassette('update_contact') do post '/api/v1/active_campaign_contacts', params: create_active_params.to_json, headers: { 'Content-Type' => 'application/json' } # 正确断言HTTP状态码 expect(response).to have_http_status(:ok) # 解析响应体校验业务结果 response_body = JSON.parse(response.body) expect(response_body['success']).to be true end end end
需要同步修正的控制器代码片段:
def update_contact contact = create_token url = URI("#{BASE_URL}/api/3/contacts/#{contact.active_campaign_id}") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Put.new(url) request['Accept'] = 'application/json' request['Content-Type'] = 'application/json' request['api-token'] = API_KEY # 修正变量名,把token改成contact data = { contact: { fieldValues: [{ field: '1', value: contact.email_token }] } } request.body = JSON.dump(data) response = http.request(request) response.code == '200' ? render(json: { success: true }) : render(json: { success: false }) end
内容的提问来源于stack exchange,提问作者theKid
相关产品推荐
相关产品推荐

