10

単体テストを作成するときに、initializeメソッドを呼び出さずにクラスをインスタンス化する必要がある場合があります。たとえば、コンストラクターが他のクラスをインスタンス化する場合は、とにかくスタブに置き換えます。例えば:

class SomeClassThatIWillTest
  def initialize
    @client = GoogleAnalyticsClient.new
    @cache = SuperAdvancedCacheSystem.new
  end

  # ...
end

テストでは、おそらく と の両方をスタブに置き換える@clientので@cache、コンストラクターが呼び出されないようにしたいと思います。それを助けることができる黒魔術はありますか?

4

3 に答える 3

24

できますよ。Class#newオブジェクトを手動で割り当てて初期化する必要がなくなる便利なメソッドにすぎません。その実装はおおよそ次のようになります。

class Class
  def new(*args, **kwargs, &blk)
    obj = allocate
    obj.send(:initialize, *args, **kwargs, &blk)
    obj
  end
end

Class#allocateを呼び出すのではなく、手動で呼び出すことができますinitialize

于 2013-06-07T14:18:44.440 に答える
4

単体テストのために、テスト対象のクラスの動作を変更しないでください。クラスがコンストラクターでより多くのアクションを持つ場合は、毎回それを模倣する必要があります。テストは維持するのが面倒になります。オブジェクト (またはクラス) を double に置き換えます。

作成済みのオブジェクトを引数としてコンストラクターに渡すことはできますか? newクラスのメソッドをスタブ化せずに double を使用できるようになります。

rspec を使用している場合は、次のことができます。

GoogleAnalyticsClient.stub(new: double)
SuperAdvancedCacheSystem.stub(new: double)

予想されるインターフェースと一致するように double を定義してください。汚いトリックは必要ありません。

于 2013-06-07T11:12:10.350 に答える
1

サブクラスでのサブクラス化SomeClassThatIWillTestと上書きinitializeはどうですか?黒魔術は関係ありません;)

このようにして、superイニシャライザを呼び出して (コードが表示された以上のものであるかどうかをテストするために)、alter@clientを呼び出してから変更することもできます@cache

このメソッドを使用した MiniTest 仕様の例:

describe MyTestClass do

  subject {
    Class.new(MyTestClass) {
      def initialize; end
    }
  }

  it "must do something" do
    subject.new.do_something.must_equal something
    # ...
  end

end
于 2013-06-07T11:14:09.710 に答える