如何让RSpec 3中Controller测试的before块仅执行一次?
如何让RSpec控制器测试中的请求只执行一次?
我懂你的困扰——每个测试用例都跑一遍create请求,不仅创建多余的用户,还拖慢测试速度。之前我也碰到过这个问题,给你两个可行的解决方案:
问题回顾
你原本的代码里,before块会在每个it测试前执行一次,所以三个测试就会发三次请求、创建三个用户。而用before(:context)时,因为let是为每个测试用例重置的状态设计的,直接调用就会触发报错。
方案一:用before(:context)+实例变量绕开let的限制
我们可以把参数和请求结果都存在实例变量里,这样整个context下只执行一次请求,同时记得清理测试数据:
require 'rails_helper' RSpec.describe V1::UsersController do describe '#create' do context 'successful create' do before(:context) do # 直接定义参数,不用let @post_params = { first_nm: Faker::Name.first_name, last_nm: Faker::Name.last_name , password: "test123456", password_confirmation: "test123456", email_address: Faker::Internet.email } # 手动初始化控制器,因为before(:context)默认没有controller实例 self.class.class_eval do let(:controller) { V1::UsersController.new } end # 执行请求并保存结果 post :create, params: @post_params @response = response @user = User.find_by_email(@post_params[:email_address]) end after(:context) do # 测试结束后清理数据,避免影响其他测试 User.find_by_email(@post_params[:email_address])&.destroy end it 'persists the user' do expect(@user).not_to be_nil end it 'stores correct user data' do @post_params.except(:password, :password_confirmation).each do |k, v| expect(@user.send(k)).to eq(v) end end it 'returns a 201 status code' do expect(@response.status).to eq(201) end end end end
方案二:合并断言到单个测试用例
如果你的测试逻辑比较简单,直接把所有断言放在一个it块里最省事,这样请求只会执行一次:
require 'rails_helper' RSpec.describe V1::UsersController do describe '#create' do let(:post_params) do { first_nm: Faker::Name.first_name, last_nm: Faker::Name.last_name , password: "test123456", password_confirmation: "test123456", email_address: Faker::Internet.email } end it 'successfully creates a user with correct data and response' do post :create, params: post_params user = User.find_by_email(post_params[:email_address]) # 所有断言放这里 expect(user).not_to be_nil post_params.except(:password, :password_confirmation).each do |k, v| expect(user.send(k)).to eq(v) end expect(response.status).to eq(201) end end end
小提醒
- 用
before(:context)时,一定要加after(:context)清理数据,不然测试数据会残留,影响其他测试的结果。 - 合并断言的方式虽然简单,但如果其中一个断言失败,整个测试都会失败,不利于快速定位问题,适合逻辑简单的场景。
内容的提问来源于stack exchange,提问作者Pants
相关产品推荐
相关产品推荐

