新手问题 如何有效的在 RSpec 中 mock 由 SQL View 生成的 ActiveRecord

treehacker · 2020年10月26日 · 最后由 lyb124553153 回复于 2020年10月30日 · 519 次阅读

我们有很多的 report 是由 SQL 直接生成的

class SaleReport < ActiveRecord::Base
  #...
  self.table_name = "sale_report"
  #..。
end

这个 sale_report 是一个 postgresql view,由 10 个以上 table join 生成的,大概这个样子:

SELECT 
       table_a.id,
       #省略若干

FROM abstract_table_a table_a
       LEFT JOIN table_u table_u ON table_u.id = table_a.payer_id
  AND table_a.payer_type = 'Farmer'
       LEFT JOIN table_b table_b ON table_b.id = table_a.order_item_id
       #省略若干LEFT JOIN
  AND table_c.archived_at IS NULL
  AND table_c.staff_id IS NOT NULL
       LEFT JOIN users table_d ON table_d.p_id = orders.p_id
  AND table_d.archived_at IS NULL
    LEFT JOIN table_f table_f ON table_b.p_id = table_f.id
  AND table_b.p_type = 'SUV'
    LEFT JOIN table_l table_f_category ON table_f_category.id = table_f.table_l_id
    LEFT JOIN table_g table_g ON table_b.p_id = table_g.id
  AND table_b.p_type = 'CAR'
       LEFT JOIN table_l table_g_category ON table_g_category.id = table_g.table_l_id
       LEFT JOIN table_h ON table_b.p_id = table_h.id
  AND table_b.p_type = 'TRUCK'
       LEFT JOIN table_r ON table_h.treatment_id = table_r.id
       LEFT JOIN table_l table_h_category
                 ON table_h_category.id = table_r.table_l_id
       LEFT JOIN (
                   #省略若干行
                 ) AS table_x ON table_a.id = table_x.inv_id

       LEFT JOIN (
                   SELECT #省略若干行
                   FROM #省略若干行
                          INNER JOIN #省略若干行
                   WHERE#省略若干行
                 ) table_Z ON table_a.id = table_Z.inv_id
WHERE #省略若干行
ORDER BY #省略若干行;

我希望的是能在 rspec 中,就像用 facotrybot 一样能够

let!(:report) {create(:sale_report)}

我尝试过创建

factory :sale_report, class: SaleReport do

但得到 error

ActiveRecord::StatementInvalid: PG::ObjectNotInPrerequisiteState: ERROR:  cannot insert into view "sale_report"
DETAIL:  Views that do not select from a single table or view are not automatically updatable.
HINT:  To enable inserting into the view, provide an INSTEAD OF INSERT trigger or an unconditional ON INSERT DO INSTEAD rule.
: INSERT INTO "sale_report" DEFAULT VALUES RETURNING "id"

现在如果想要写这个 report 的单元测试需要一个个的 creat join table 的 instance,非常的耗时,而且需要先理解 tables 之间的关系。

请问有什么思路吗?有什么 gem 可以帮助实现吗

有一个办法,是写一个 helper 在测试环境生产这个 view。

springwq 回复

你是说逐个的 create table 里的 record 吗

直接 mock SaleReport 查询的返回

piecehealth 回复

那就没有意义了,我的目的就是看通过改变某个输入值,看结果是不是想要的

treehacker 回复

你要是测 SaleReport,就往组成 view 的表里插数据。你的测试要依赖 SaleReport,就直接 mock。

@piecehealth 问题是往这种组成 view 的表里插数据是非常麻烦的事情,我们有很多这种类似的 report,之前的 developer 并没有很好地 setup 测试,我现在每次写单元测试都要先搞明白这个 report 是怎么组成的,我在想能不能有更好的方法,能够自动生成这些 associated records

treehacker 回复

这是有人挖坑需要你来填的问题,就算你能找到捷径,也是坑上加坑,不是填坑。

你想测一个本身很复杂的东西,你对它又不够了解,那是没法测的。

真正解决方法也不是技术上的了:短期来说要让你老板知道这坑不是你挖的,要不就绕着走,要不就填。要填的话又需要多少工作量多少收益跟老板讲清楚。通过一些“技巧”坑上蹦迪是最害人的。长期来说是你们团队 code review、测试覆盖不规范,不纠正的话代码会走上超难维护的道路。

既然以前的人没写测试,你现在补测试就吃力不讨好。 历史遗留问题,leader 估计也不能接受你没产出跑去补测试, 直接把测试环境的数据导到本地,直接跑一下接口当成测试

需要 登录 后方可回复, 如果你还没有账号请 注册新账号