RSpec请求测试中let语句延迟加载导致数据未入库的解决方案咨询
嘿,这个问题我之前也踩过坑,完全懂你的困扰——let的延迟加载确实在请求测试里容易出这种问题,但真的没必要直接放弃它的复用性优势!给你几个实用的解决方案,完全不用重构所有现有测试:
方案1:提前触发let变量的求值
你猜得没错,主动触发let变量的实例化其实是个非常高效的临时方案,而且一点也不“不优雅”,反而很贴合RSpec的设计逻辑。只需要在执行get请求前,手动引用一下需要提前创建的变量就行:
describe "GET #index" do it "responds with all the orders of the customer" do # 提前触发变量实例化,把数据写入测试数据库 order_1 && order_2 get customer_orders_path(id: customer.id) expect(json_body.count).to eq(2) end end
如果想让代码更清晰,也可以用before块统一处理:
describe "GET #index" do before do # 调用let变量,触发它们的创建逻辑 customer order_1 order_2 end it "responds with all the orders of the customer" do get customer_orders_path(id: customer.id) expect(json_body.count).to eq(2) end end
这个方法的好处是完全不用改动其他测试里的let定义,只在受影响的测试里加几行代码,保留let的复用性。
方案2:对需要提前加载的变量改用let!
如果某个let变量是大部分测试都需要提前存在的,那直接把它改成let!就好——let!是立即加载的,会在每个测试用例运行前就完成实例化,而不是等到第一次引用时才执行:
RSpec.describe Api::OrdersController, type: :request do let(:customer) { FactoryGirl.create(:customer) } # 改成let!,测试运行前就会创建这两个订单 let!(:order_1) { FactoryGirl.create(:order, customer: customer) } let!(:order_2) { FactoryGirl.create(:order, customer: customer) } describe "GET #index" do it "responds with all the orders of the customer" do get customer_orders_path(id: customer.id) expect(json_body.count).to eq(2) end end end
注意不要把所有let都改成let!,因为let!会增加测试的启动时间(每次测试都要提前创建数据),只给确实需要提前加载的变量用就好。
方案3:用共享上下文封装提前加载逻辑
如果有一组测试都需要相同的提前加载逻辑,可以把这些代码封装成共享上下文,避免重复编写before块:
RSpec.shared_context "with customer orders" do let(:customer) { FactoryGirl.create(:customer) } let(:order_1) { FactoryGirl.create(:order, customer: customer) } let(:order_2) { FactoryGirl.create(:order, customer: customer) } before do # 统一触发变量实例化 customer order_1 order_2 end end RSpec.describe Api::OrdersController, type: :request do describe "GET #index" do include_context "with customer orders" it "responds with all the orders of the customer" do get customer_orders_path(id: customer.id) expect(json_body.count).to eq(2) end end # 其他需要相同测试数据的用例,直接include这个上下文就行 end
这个方案既保持了let的复用性,又统一管理了提前加载的逻辑,不会让代码分散在各个测试里。
总结一下:这几个方案都不用放弃let的优势,根据你的实际场景选就行——少数测试有问题用方案1,部分变量需要全局提前加载用方案2,多组测试需要相同逻辑用方案3。
内容的提问来源于stack exchange,提问作者Alexandru Robert
相关产品推荐
相关产品推荐

