除了 expect_any_instance_of(#{ControllerClass}).to 接收之外,是否有更好的方法来测试在控制器中调用脚本?

Is there a better way to test that a script is being called in a controller other than expect_any_instance_of(#{ControllerClass}).to receive?

我刚开始使用 RSpec,所以还有很多我不知道的地方。我目前正在研究在按下按钮时应该 运行 脚本的一部分功能。脚本目前是在controller中调用的,不知道有没有好的测试方法

我目前正在使用

expect_any_instance_of(ConfigurationsController)
  .to receive(:system)
  .with('sh bin/resque/kill_resque_workers')
  .and_return(true)

在功能规范中它有效,但 rubocop 抱怨使用 expect_any_instance_of 并且我被告知只有在没有更好的方法时才使用该方法。

有没有更好的方法来测试这个?比如有没有办法获取正在使用的控制器实例,或者对此有更好的测试?

更好的模式是首先不要在控制器中内联系统调用。而是创建一个单独的对象,它知道如何终止您的工作进程并从您的控制器调用它。 service object pattern 通常用于此。它使 stub/spy/mock 依赖项变得更容易,并确保它在您的应用程序边界处停止。

它还可以让您单独测试对象。测试普通的旧 ruby 对象真的很容易。测试控制器不是。

module WorkerHandler
  def self.kill_all
    system 'sh bin/resque/kill_resque_workers'
  end
end

# in your test
expect(WorkerHandler).to receive(:kill_all)

如果您的服务对象方法在 class 的实例上运行,您可以使用 stub_const 来存根新方法,以便它 returns mocks/spies.

另一个更新颖的解决方案是通过 Rack 中间件进行依赖注入。你刚刚write a piece of middleware that injects your object into envenv 是从中间件堆栈一直传递到您的应用程序的状态变量。例如,这就是 Warden 的工作方式。当您对控制器进行 http 调用或使用 before { session.env('foo.bar', baz) }.

时,您可以在规范中传递 env