Rspec控制器测试中Stub map_typeform_response报错问题排查
关于Typeform响应获取测试中Stub方法与NilClass错误的排查
问题描述
我正在测试控制器的fetch_typeform_response方法,尝试Stub涉及外部API的:getResponse和:map_typeform_response方法,但运行测试时出现错误:HealthProfile does not implement #map_typeform_response。移除Stub代码后,又出现map_typeform_response方法内部的NoMethodError(undefined method '[]' for nil:NilClass),确认该方法确实被调用了,想请教这是什么原因?
相关代码
控制器方法
def fetch_typeform_response hp = HealthProfile.find(params[:id]) form = TypeformService.new('x') response = form.getResponse("query=#{ hp[:id] }") if response['total_items'] != 1 if response[:response_id].present? response = form.getResponse("included_response_ids=#{ hp[:response_id] }") end end if response['total_items'] == 1 response = response['items'].first health_profile = HealthProfile.map_typeform_response(response) if health_profile.save health_profile.reload redirect_to health_profile_path(health_profile), notice: "Successfully updated the health profile response." return end end redirect_to health_profiles_path, notice: "We could not locate the health profile." end
测试代码
it "expects to fetch typeform response" do new_hp = build(:health_profile) new_hp_after_mapping = build(:health_profile) allow_any_instance_of(TypeformService).to receive(:getResponse).and_return({ 'total_items': 1, 'items': [ new_hp ] }.as_json) allow_any_instance_of(HealthProfile).to receive(:map_typeform_response).and_return(new_hp_after_mapping) get :fetch_typeform_response, params: { id: @hp.id } expect(response).to redirect_to(health_profile_path(@hp.id)) end
错误原因分析与解决方案
1. 第一个错误:HealthProfile does not implement #map_typeform_response
你在测试中Stub的是HealthProfile的实例方法,但控制器里调用的HealthProfile.map_typeform_response(response)是一个类方法!这就是报错的核心原因——Stub的方法类型完全不匹配。
解决方案:
把实例方法的Stub改为类方法的Stub:
# 替换原来的allow_any_instance_of代码 allow(HealthProfile).to receive(:map_typeform_response).and_return(new_hp_after_mapping)
2. 第二个错误:undefined method '[]' for nil:NilClass
移除Stub后出现这个错误,主要有两个可能的诱因:
- 测试数据结构不符:你Stub的
getResponse返回的items里是HealthProfile实例,而真实的Typeform API返回的应该是哈希结构。当map_typeform_response尝试用[]访问这个实例的属性时,自然会触发错误。 - 真实API返回异常:如果没有Stub,
form.getResponse可能返回nil,或者返回的哈希中items为空,导致response['items'].first变成nil,传入map_typeform_response后触发nil的[]调用错误。
解决方案:
- 修正测试中的模拟数据,确保返回符合真实API结构的哈希:
allow_any_instance_of(TypeformService).to receive(:getResponse).and_return({ 'total_items': 1, 'items': [ { # 这里填充和真实Typeform响应一致的键值对,比如"answers": [...], "respondent_id": "xxx"等 } ] }.as_json) - 在
map_typeform_response类方法中添加nil检查,提前拦截无效输入:def self.map_typeform_response(response) return nil unless response.present? # 后续的映射逻辑 end - 在控制器中增加对
getResponse返回值的校验,避免后续处理nil:response = form.getResponse("query=#{ hp[:id] }") unless response.present? redirect_to health_profiles_path, notice: "Failed to fetch Typeform response." return end
内容的提问来源于stack exchange,提问作者Jeremy Thomas
相关产品推荐
相关产品推荐

