Rspec测试报NoMethodError: undefined method `response_code' for nil:NilClass
问题原因
出现这个问题的核心原因是测试代码混用了两套互不兼容的测试工具链:
- 你手动引入了
Rack::Test::Methods,调用的post方法是这个库提供的,请求发出去后,响应结果存在Rack::Test自己维护的last_response对象里,不会给Rails控制器测试默认使用的@response实例变量赋值。 - 你调用的
assert_response :success是Rails内置测试框架提供的断言方法,它默认从@response对象上读取response_code做校验,这个变量因为没被赋值是nil,自然会抛出undefined method 'response_code' for nil:NilClass错误。
日志里打印的Completed 200 OK只代表请求确实被Rails应用接收并正常处理完成,和测试框架里的断言对象赋值是两个独立环节,不代表测试侧的状态是通的。
解决方法
二选一即可,不要混用不同测试库的请求方法和断言方法:
方案1:使用Rails原生测试写法(推荐)
不需要手动引入Rack::Test::Methods,直接用Rails测试封装好的方法即可,文件上传用Rails自带的fixture_file_upload工具,和assert_response完全适配:
require 'rails_helper' RSpec.describe ApplicationController do before do @file = fixture_file_upload('./image.png', 'image/png') end it 'can upload a file' do params = { files: [@file], phone: '000000000', email: 'example@gmail.com', name: 'example', } post '/form-data/post', params assert_response :success end end
方案2:全程使用Rack::Test的语法
如果要坚持用Rack::Test::Methods发请求,就不要用Rails的assert_response,改用Rack::Test自带的响应对象做断言:
require 'rails_helper' RSpec.describe ApplicationController do include Rack::Test::Methods before do @tempfile = File.new('./image.png') @file = Rack::Test::UploadedFile.new(@tempfile, 'image/png', true) end it 'can upload a file' do params = { files: [@file], phone: '000000000', email: 'example@gmail.com', name: 'example', } post '/form-data/post', params # Rack::Test的响应存在last_response里,直接校验状态码即可 expect(last_response.status).to eq(200) end end
这类问题基本都是写测试时随便拼接不同来源的代码片段导致的,看着方法名都是
post、都是做成功断言,底层属于完全独立的工具实现,状态不互通就会出现这种“请求明明跑通了但断言报错”的诡异情况。
内容的提问来源于stack exchange,提问作者Esoul
相关产品推荐
相关产品推荐

