Rspec控制器assigns断言不符合预期问题排查求助
你遇到的这个问题核心是测试数据没有被正确隔离——当前测试例子中拿到的Books并不是你在这个例子里创建的那条,而是之前其他测试残留下来的数据。
Rspec默认会在每个测试例子执行前后清理数据库,但对于Mongoid(MongoDB的ORM框架)来说,默认的清理机制支持有限,导致数据在测试之间残留,最终让你的断言失败:你预期只有当前创建的1条Book,实际却返回了之前测试留下的多条数据。
另外还有个小细节可以优化:你控制器里的分页写法Kaminari.paginate_array(Book.all).page(params[:page])完全没必要,因为Mongoid的查询集合本身就支持Kaminari的分页方法,直接写成Book.all.page(params[:page])即可,还能避免把所有数据加载到内存再分页的性能问题。
1. 配置DatabaseCleaner清理MongoDB测试数据
我们可以用database_cleaner-mongoid这个gem来确保每个测试后彻底清理MongoDB的数据,解决测试间数据残留的问题:
第一步:添加Gem到测试环境
在你的Gemfile的test组中新增依赖:
group :test do gem 'rspec-rails', '~> 3.7' gem 'database_cleaner-mongoid' # 新增此行 end
执行bundle install完成安装。
第二步:在Rspec配置中启用DatabaseCleaner
打开spec/rails_helper.rb,在RSpec的配置块中添加以下内容:
RSpec.configure do |config| # 测试套件启动前,先彻底清理一次数据库 config.before(:suite) do DatabaseCleaner[:mongoid].strategy = :truncation DatabaseCleaner.clean_with(:truncation) end # 每个测试例子执行前后,自动清理数据库 config.around(:each) do |example| DatabaseCleaner.cleaning do example.run end end end
这个配置会用截断集合的方式,在每个测试执行前后清空MongoDB的所有数据,确保每个测试都在干净的环境中运行。
2. 优化测试断言(可选但推荐)
因为assigns(:books)返回的是Kaminari的分页对象(而非普通数组),直接用eq([book])可能存在潜在问题。你可以把断言改成以下两种方式之一:
# 忽略顺序的匹配 expect(assigns(:books)).to match_array([book]) # 转成普通数组后再断言 expect(assigns(:books).to_a).to eq([book])
重新运行你的测试,现在每个测试例子都会在无残留数据的干净环境中执行,你创建的Book会是数据库里唯一的一条,断言就能正常通过了。
内容的提问来源于stack exchange,提问作者ruby_dev

