簡體   English   中英

如何獲得正在通過機架測試測試的Sinatra應用程序實例?

[英]How do I get the Sinatra app instance that's being tested by rack-test?

我想掌握通過機架測試測試的應用程序實例,以便可以模擬其某些方法。 我以為我可以將應用程序實例保存在app方法中,但是由於某些奇怪的原因而無法正常工作。 似乎rack-test只是使用實例來獲取類,然后創建自己的實例。

我已經進行了測試以證明我的問題(它需要運行“ sinatra”,“ rack-test”和“ rr”等寶石):

require "sinatra"
require "minitest/spec"
require "minitest/autorun"
require "rack/test"
require "rr"

describe "instantiated app" do
  include Rack::Test::Methods

  def app
    cls = Class.new(Sinatra::Base) do
      get "/foo" do
        $instance_id = self.object_id

        generate_response
      end

      def generate_response
        [200, {"Content-Type" => "text/plain"}, "I am a response"]
      end
    end

    # Instantiate the actual class, and not a wrapped class made by Sinatra
    @app = cls.new!

    return @app
  end

  it "should have the same object id inside response handlers" do
    get "/foo"

    assert_equal $instance_id, @app.object_id,
      "Expected object IDs to be the same"
  end

  it "should trigger mocked instance methods" do
    mock(@app).generate_response {
      [200, {"Content-Type" => "text/plain"}, "I am MOCKED"]
    }

    get "/foo"

    assert_equal "I am MOCKED", last_response.body
  end
end

為什么不使用我提供的實例進行rack-test 如何掌握rack-test正在使用的實例,以便可以模擬generate_response方法?


更新資料

沒有進步。 事實證明,在發出第一個請求(即get("/foo") )時, rack-test會即時創建被測試的實例,因此在此之前不可能模擬該應用程序實例。

我已經用rr的stub.proxy(...)截獲.new.new! .allocate ; 並添加了帶有實例的類名和object_id的puts語句。 我還在測試過的類的構造函數以及請求處理程序中添加了此類語句。

這是輸出:

From constructor: <TestSubject 47378836917780>
Proxy intercepted new! instance: <TestSubject 47378836917780>
Proxy intercepted new instance: <Sinatra::Wrapper 47378838065200>
From request handler: <TestSubject 47378838063980>

注意對象ID。 測試的實例(從請求處理程序打印)從未經歷過.new且從未初始化。

因此,令人困惑的是,從未創建被測試的實例,但無論如何仍然存在。 我的猜測是正在使用allocate ,但是代理攔截顯示它沒有使用。 我運行了TestSubject.allocate來驗證攔截器是否正常工作。

我還向測試的類添加了inheritedincludedextendedprepended鈎子,並添加了打印語句,但是它們從未被調用過。 這讓我完全無法理解到底要進行什么樣的可怕的黑魔法測試。

總結一下:發送第一個請求時, 即刻創建了測試實例。 被測試的實例是由惡魔魔術師創建的,並通過鈎子回避了試圖捕獲它的所有嘗試,因此我找不到模擬它的方法。 幾乎感覺就像rake-test的作者費了rake-test力氣才能確保在測試過程中不會碰到該應用實例。

我仍在摸索尋找解決方案。

好的,我終於明白了。

一直Sinatra::Base.call ,問題一直出在Sinatra::Base.call 在內部,它執行dup.call!(env) 換句話說,每次您運行call ,Sinatra都會復制您的應用程序實例,並將請求發送到重復的實例,從而繞開所有模擬和存根。 這說明了為什么沒有觸發生命周期掛鈎的原因,因為大概dup使用了一些低級C魔術來克隆實例(需要引用)。

rack-test根本不做任何令人費解的事情,它所做的全部只是調用app()來檢索應用程序,然后在應用程序上調用.call(env) 然后,我需要做的就是在類中添加.call方法,並確保Sinatra的魔力未插入任何地方。 我可以使用.new! 在我的應用程序上阻止Sinatra插入包裝器和堆棧,並且我可以使用.call! 在沒有Sinatra復制我的應用實例的情況下調用我的應用。

注意:我不再只能在app函數內創建一個匿名類,因為每次調用app()時都會創建一個新類,而使我無法模擬它。

這是問題的測試,已更新為可運行:

require "sinatra"
require "minitest/spec"
require "minitest/autorun"
require "rack/test"
require "rr"

describe "sinatra app" do
  include Rack::Test::Methods

  class TestSubject < Sinatra::Base
    get "/foo" do
      generate_response
    end

    def generate_response
      [200, {"Content-Type" => "text/plain"}, "I am a response"]
    end
  end

  def app
    return TestSubject
  end

  it "should trigger mocked instance methods" do
    stub(TestSubject).call { |env|
      instance = TestSubject.new!

      mock(instance).generate_response {
        [200, {"Content-Type" => "text/plain"}, "I am MOCKED"]
      }

      instance.call! env
    }

    get "/foo"

    assert_equal "I am MOCKED", last_response.body
  end
end

是的,機架測試會為每個請求實例化新app (可能是避免沖突並以新狀態開始。)這里的選項是在app內部模擬Sinatra::Base派生類本身:

require "sinatra"
require "minitest/spec"
require "minitest/autorun"
require "rack/test"
require "rr"

describe "instantiated app" do
  include Rack::Test::Methods

  def app
    Class.new(Sinatra::Base) do
      get "/foo" do
        generate_response
      end

      def generate_response
        [200, {"Content-Type" => "text/plain"}, "I am a response"]
      end
    end.prepend(Module.new do # ⇐ HERE
      def generate_response
        [200, {"Content-Type" => "text/plain"}, "I am MOCKED"]
      end
    end).new!
  end

  it "should trigger mocked instance methods" do
    get "/foo"

    assert_equal "I am MOCKED", last_response.body
  end
end

或整體模擬app方法。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM