3

モデルインスタンスに含まれるデコレータモジュールを使用します(「extends」メソッドを使用)。だから例えば:

module Decorator
  def foo
  end
end

class Model < ActiveRecord::Base
end

class ModelsController < ApplicationController
  def bar
    @model = Model.find(params[:id])
    @model.extend(Decorator)
    @model.foo
  end
end

それから私はテストで次のことをしたいと思います(モカを使用して):

test "bar" do
  Model.any_instance.expects(:foo).returns("bar")
  get :bar
end 

これはどういうわけか可能ですか、それともこの機能を取得する他の方法を考えていますか?

4

4 に答える 4

2

単なる仮定注:デコレータfooメソッドが、送信したコードに示されていない「bar」を返すと仮定します。これを想定しないと、メソッドが「bar」ではなくnilを返すため、期待はとにかく失敗します。

上記のように、私はあなたが裸の真新しいレールアプリケーションでそれを持っているので全体の話を試しました、そして私はこれができないことに気づきました。これは、テストでexpectsメソッドが呼び出されたときに、メソッド'foo'がクラスModelにアタッチされていないためです。

私は、期待している間に呼び出されたメソッドのスタックをたどろうとして、この結論に達しました。Mocha :: Centralでスタブを呼び出し Mocha :: ClassMethodスタブを呼び出します。Mocha :: AnyInstanceMethodで*hide_original_method*を呼び出します。そこで、* hidden_​​original_method *は非表示にするメソッドを見つけず、何もしません。次に、Model.fooメソッドはスタブされたmochaメソッドにエイリアスされません。これは、mochaの期待値を実装するために呼び出す必要がありますが、実際のModel.fooメソッドが呼び出されます。これは、コントローラー内のModelインスタンスに動的にアタッチするメソッドです。

私の答えはそれができないということです。

于 2011-10-09T16:50:46.100 に答える
1

ああ、わかってるよ。外部サービスへの呼び出しをスタブアウトしたい。mocha がこの方法で拡張できないのは興味深いことです。上記以外にも、スタブ化されたメソッドがモジュールではなくシングルトンクラスで定義されているためと思われるため、混同しないでください。

なぜこのようなものではないのですか?

test "bar" do
  Decorator = Module.new{ def foo; 'foo'; end }
  get :bar
end

Decorator がすでに定義されているという警告が表示されたくない場合 (これは何らかの結合が行われていることを示しています)、それを挿入できます。

class ModelsController < ApplicationController
  class << self
    attr_writer :decorator_class
    def decorator_class; @decorator_class ||= Decorator; end
  end

  def bar
    @model = Model.find(params[:id])
    @model.extend(self.class.decorator_class)
    @model.foo
  end
end

これにより、テストは次のようになります。

test "bar" do
  dummy = Module.new{ def foo; 'foo'; end }
  ModelsController.decorator_class = dummy
  get :bar
end

もちろん、複数のデコレータや複数のメソッドを定義するデコレータなど、より複雑な状況の場合、これはうまくいかないかもしれません。

しかし、発見をスタブするよりはましだと思います。通常、統合テストでモデルをスタブ化することは望ましくありません。

于 2011-10-12T02:23:40.117 に答える
1

動作します (render :text を使用したテスト アプリケーションで確認済み)。

私は通常、(実行時にデコレーターを拡張する代わりに) デコレーターを含めます。

module Decorators
  module Test
    def foo
      "foo"
    end
  end
end

class MoufesController < ApplicationController

  def bar
    @moufa = Moufa.first
    @moufa.extend(Decorators::Test)
    render :text => @moufa.foo
  end
end

require 'test_helper'

class MoufesControllerTest < ActionController::TestCase
  # Replace this with your real tests.
  test "bar" do
    m = Moufa.first
    Moufa.expects(:find).returns(m)
    m.expects(:foo).returns("foobar")

    get :bar, {:id => 32}
    assert_equal @response.body, "foobar"
  end
end
于 2011-10-09T15:21:27.363 に答える
0

:bar の戻り値をテストしたい場合の小さな変更 -

test "bar" do
  Model.any_instance.expects(:foo).returns("bar")
  assert_equal "bar", get(:bar)
end

しかし、モデル インスタンスにデコレータ メソッドがあることをテストしているだけなら、本当にそれをテストする必要があるのでしょうか? その場合、Object#extend をテストしているようです。

@model.foo の動作をテストしたい場合は、統合テストでそれを行う必要はありません。これがデコレーターの利点であり、次のように分離してテストできます。

x = Object.new.extend(Decorator)
#.... assert something about x.foo ...

私の経験では、通常、統合テストでのモックはコードの匂いです。

于 2011-10-09T15:28:32.973 に答える