【问题标题】:An Rspec helper like assert_database_unchanged?像 assert_database_unchanged 这样的 Rspec 助手?
【发布时间】:2020-04-19 07:25:37
【问题描述】:

是否有用于 postgresql 的 rspec 扩展,允许人们测试这样的东西?

expect { make_bad_request }.to not_change_database

即确保没有创建、更新或删除任何内容。

当然,我们可以检查一个特定的表,但大多数情况下我们只是想确保没有任何变化,没有在一些多阶段保存中偷偷摸摸。

使用一个小助手并不是特别容易,因为尽管 postgres 有pg_stat_database,但它在测试事务期间没有更新。我仍然可以看到它是如何可行的,但它会有点麻烦。有人做过吗?

更新: 我被要求举例说明这可能如何有用。

HTTP 的约定是,如果请求返回错误状态,则应用程序状态不会发生任何变化。该约定的例外情况很少见,我们喜欢约定优于配置。

活动记录有助于通过默认验证的工作方式来强制执行此操作,但它仍然留下许多出错的机会,特别是对于具有原子性最重要的复杂事件链。

因此,为了轻松执行 HTTP 约定,您可以将其比上述更进一步,而是使用类似指令的东西表达为类似的东西

describe 'error responses', :database_changes_disallowed do
  context 'invalid form data' do

    before do
    ...setup belongs only here
    end

    it 'returns 422' do
      ...
    end
  end
end

Rspec 已经能够使用数据库事务来隔离每个示例级别的状态更改,这将旨在进一步细分 beforeit

如果您足够明智地确保您的数据库仅存储应用程序状态而没有像User#last_active_at 这样的伪日志记录,这将适用于设计良好的应用程序。没有的话马上就知道了。

它将大大增加针对一些最严重的状态损坏错误的测试用例覆盖率,同时需要更少的代码并消除一些测试复杂性。突然为以前的工作测试进行数据库更改的情况可能是由于架构朝着不幸的方向发生变化,是真正的和不寻常的需要例外或严重的错误。 p>

如果事实证明它在技术上不可行,我会很难过,但就应用程序设计而言,这似乎不是一个坏主意。

【问题讨论】:

  • 我只会编写更具体的断言来检查相关表是否已被更改。虽然乍一看似乎是个好主意,但我认为这里的魔鬼确实存在于细节中,Rails 没有提供类似的东西是有原因的。
  • 有什么特别的细节吗? “有问题的表格”的问题是正在测试的事情之一。我们必须假设我们的控制器只影响我们认为的表。我猜 Rails 没有附带它,因为他们相信 db 不可知论,而一些 dbs 不支持这一点。就个人而言,我不相信不可知论在实践中是可以实现的,所以我很高兴使用 DB 特定的附加组件。
  • 我不确定不可知论是唯一的问题,而是它找到一个可靠的指标,在​​交易中工作并且不会给你错误的否定/肯定。测试您的代码根本没有任何意外的副作用通常不是一个非常可行的策略。测试最可能的副作用或已知错误。
  • 是的,如果可靠地实现起来非常困难,那么,是的,这些都属于合理假设范围并且是可以忍受的。
  • 像往常一样,很难(甚至不可能)证明某事没有发生。但是很容易证明某事已经完成。您能否添加一个您认为应该具有证明数据库中没有记录被更改的规范的方法示例?并详细说明为什么您认为指定记录数没有变化或特定记录没有变化是不够的?

标签: ruby-on-rails ruby postgresql rspec


【解决方案1】:

这是一个棘手的问题,因为很难判断您的应用中不应该发生什么。 IMO 最好将您的规范重点放在应用程序应该做什么上。

换句话说:如果您想测试没有对数据库进行更改,您是否应该检查是否没有写入文件?并且没有提出任何要求?您是否应该测试没有更改任何文件权限?

我想你明白我的意思。

但是,我不知道这样做可能有正当理由。在这种情况下,我会使用类似db-query-matcher

 expect { your_code_here }.not_to make_database_queries(manipulative: true)

我通常使用并看到它被用于 N+1 测试(当您想指定特定查询被调用多少次时),但似乎这个匹配器也适用于您。

但它可能非常脆弱:如果您将此类检查添加到大多数测试中,并且您的应用正在发展,那么您可能会因为某些操作开始需要数据库更新而导致规范失败。您的来电。

【讨论】:

  • 嗯,是的,只有让事情变得更轻松、更安全而不是增加脆弱性,这才是值得的。如果在某个时候您决定确实想要以前没有的数据库突变,那么这似乎是一个脆弱的好地方。您的答案会起作用,但仍然可以触发查询并在错误时回滚它们。确实,在 postgres 中,问题在于事务不是递归概念的方式,但 activerecord 通过使用保存点假装它是。
  • 是的,我确实认为在没有副作用控制的情况下,如果有更简单的方法来表达“这种故障模式应该创建零状态变化,那么函数式语言会给你带来好处”在我的应用程序中”。不这样做的原因似乎纯粹是因为实施困难。
  • 为什么不只是假设数据库事务正常工作,并检查(无论您在做什么)是否在事务下完成?
  • 您最有可能通过将各种save! 包装在事务中来确保原子性。当第二次调用返回 422 时,第一次被回滚,数据库保持不变,但我假设这个 gem 会说发生了一个操纵查询。 (或者它是否考虑交易?)
  • 至于为什么不检查生产代码是否编写正确,这就是测试的目的。
【解决方案2】:

我想你正在寻找

assert_no_changes(expressions, message = nil, &block)

https://api.rubyonrails.org/v6.0.2.1/classes/ActiveSupport/Testing/Assertions.html#method-i-assert_no_changes

【讨论】:

  • 很遗憾没有。该块的主题是棘手的部分。它需要知道数据库数据是否发生了变化。
猜你喜欢
  • 2021-03-07
  • 2014-08-06
  • 2016-05-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-01-04
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多