Насмешка / заглушка метода, который включен из "instance.extend (DecoratorModule)" - PullRequest
2 голосов
/ 09 октября 2011

Я использую модуль декоратора, который включается в экземпляр модели (с помощью метода "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 ]

1 голос
/ 12 октября 2011

Хорошо, теперь я понимаю. Вы хотите отключить звонок на внешний сервис. Интересно, что мокко не работает с таким способом. Помимо того, что было упомянуто выше, похоже, что методы-заглушки определены в синглтон-классе, а не в модуле, поэтому не путайтесь.

Почему бы не что-то подобное?

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

Конечно, если у вас более сложная ситуация с несколькими декораторами или декораторами, определяющими несколько методов, это может не сработать для вас.

Но я думаю, что это лучше, чем заглушить находку. Как правило, вы не хотите заглушать свои модели в интеграционном тесте.

1 голос
/ 09 октября 2011

Только предположение Примечание: я предполагаю, что ваш Decorator foo метод возвращает "bar", который не отображается в коде, который вы отправили. Если я этого не предполагаю, тогда ожидания все равно потерпят неудачу, потому что метод возвращает nil, а не "bar".

Исходя из вышеизложенного, я перепробовал всю историю, как у вас, с помощью совершенно нового приложения рельсов, и я понял, что это невозможно. Это связано с тем, что метод 'foo' не присоединен к классу Модель , когда ожидает, что метод будет вызван в вашем тесте.

Я пришел к такому выводу, пытаясь проследить за стеком вызываемых методов, в то время как в ожидается . ожидает вызовов заглушек в Mocha :: Central , что вызывает заглушек в Mocha :: ClassMethod , что вызывает * hide_original_method * in Mocha :: AnyInstanceMethod . Там * hide_original_method * не находит никакого способа скрыть и ничего не делает. Тогда метод Model.foo не привязывается к методу-заглушке, который должен вызываться для реализации вашего ожидания мокко, но вызывается фактический метод Model.foo , который вы вызываете. динамически подключаться к экземпляру модели внутри контроллера.

Мой ответ таков: это невозможно.

1 голос
/ 09 октября 2011

Работает (подтверждено в тестовом приложении с визуализацией: текст)

Я обычно включаю декораторы (вместо того, чтобы расширять их во время выполнения), и я избегаю any_instance, поскольку это считается плохой практикой (вместо этого я высмеиваю find).

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
0 голосов
/ 09 октября 2011

Одно незначительное изменение, если вы хотите проверить возвращаемое значение: bar -

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

Но если вы просто проверяете, что у экземпляра модели есть метод (ы) декоратора, вам действительно нужно проверить это? Похоже, вы тестируете Object # extension в этом случае.

Если вы хотите проверить поведение @ model.foo, вам не нужно делать это в интеграционном тесте - это преимущество декоратора, вы можете затем протестировать его изолированно, как

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

По моему опыту, издевательство в интеграционных тестах - это обычно запах кода.

...