当 运行 RSpec 测试时,从 Ruby 调用 shell 命令挂起

Calling a shell command from Ruby hangs when running RSpec test

以下情况会产生预期结果:

  1. 从 zsh,执行:

    /usr/local/bin/pg_dump mydatabase  #=> a bunch of sql output
    
  2. 从 irb 或 rails 控制台,执行:

    `/usr/local/bin/pg_dump my_database` #=> a bunch of sql output
    
  3. 从一个方法中调用相同的 shell 命令:

    class AppDb
      def contents
        `/usr/local/bin/pg_dump my_database`
      end
    end
    
    # from rails console:
    AppDb.new.contents #=> a bunch of sql stuff
    

但是,当我从 Rspec 测试完全相同的 AppDb class 时,shell 命令在断言处无限期挂起:

expect(AppDb.new.contents).to match "PostgreSQL database dump complete"

你知道为什么会这样吗?

挂起是由于:

  1. spec_helper.rb

    中的rspec配置

    config.use_transactional_fixtures = 真

    这会启动一个 postgresql BEGIN 块,它会启动对表的锁定

  2. 结合使用pg_dump,由于数据库锁无法执行。它挂起(永远或指定的超时)等待删除锁。

所以解决方案就是将 use_transactional_fixtures 设置为 false。当然,这会在规范末尾留下带有测试数据的数据库,必须以另一种方式处理。 DatabaseCleaner可以用,但是不能用事务策略,因为这样也会有同样的问题。