Метод заглушки вызывается из конструктора при использовании Fabrication - PullRequest
1 голос
/ 16 января 2012

У меня есть следующая модель

class User < ActiveRecord::Base
  before_create :set_some_values

  private
  def set_some_values
    #do something
  end
end

В спецификациях я использую Изготовление gem для создания объектов, но я не могу найти способ заглушить set_some_valuesметод.Я пытался

User.any_instance.stub!(:set_some_values).and_return(nil)

, но Изготовление, кажется, игнорирует это.Можно ли это сделать?

Ответы [ 2 ]

2 голосов
/ 17 мая 2012

Вот почему мне не нравятся обратные вызовы ActiveRecord - потому что, если вы не хотите иметь ничего общего с обратным вызовом (потому что, скажем, вы делаете вызов внешней службе внутри обратного вызова), вам все равно придется быть обеспокоенным о заглушении этого. Да, вы можете заглушить методы внутри обратного вызова, но это та же проблема, и на самом деле это немного хуже, потому что теперь вы беспокоитесь о чем-то внутри метода, с которым вы не хотите иметь ничего общего.

Как обычно, здесь есть несколько вариантов.

Одна из опций, которую я часто использовал в прошлом, - добавить условие к вашему обратному вызову, которое по умолчанию отключает его. Таким образом, ваш класс Post может выглядеть так:

class Post
  before_save :sync_with_store, :if => :syncing_with_store?

  def syncing_with_store?; @syncing_with_store; end
  attr_writer :syncing_with_store

  def sync_with_store
     # make an HTTP request or something
  end
end

Теперь, где вы действительно хотите вызвать обратный вызов (возможно, в вашем контроллере или где-то еще), вы можете установить post.syncing_with_store = true перед тем, как позвонить post.save.

Недостатком этого подхода является то, что вы (и другие разработчики, работающие с вами) должны иметь в виду, и не совсем очевидно, что вы должны это делать. С другой стороны, если вы забудете это сделать, ничего плохого не случится.

Другой вариант - использовать поддельный класс. Скажем, у вас есть сообщение, которое отправляет свои данные во внешнее хранилище данных при сохранении. Вы можете извлечь код, выполняющий push-запрос, в отдельный класс (например, Pusher), который будет доступен в Post.pusher_service. По умолчанию, однако, это было бы установлено на ложный класс Pusher, который отвечает на тот же интерфейс, но ничего не делает. Так как:

class Post
  class << self
    attr_accessor :pusher_service
  end
  self.pusher_service = FakePostPusher

  before_save :sync_with_store

  def sync_with_store
    self.class.pusher_service.run(self)
  end
end

class FakePostPusher
  def self.run(post)
    new(post).run
  end

  def initialize(post)
    @post = post
  end

  def run
    # do nothing
  end
end

class PostPusher < FakePostPusher
  def run
    # actually make the HTTP request or whatever
  end
end

В вашем файле рабочей среды вы должны установить Post.pusher_service = Pusher. В отдельных тестах или тестовых примерах вы создаете подкласс Post - let(:klass) { Class.new(Post) } - и устанавливаете klass.pusher_service = Pusher (таким образом, вы не устанавливаете его постоянно и не влияете на будущие тесты).

Третий подход, над которым я экспериментировал, заключается в следующем: просто не используйте обратные вызовы ActiveRecord. Это то, что я взял из скринкастов Гэри Бернхардта (которые, кстати, довольно удивительны). Вместо этого определите класс обслуживания, который оборачивает процесс создания сообщения. Что-то вроде:

class PostCreator
  def self.run(attrs={})
    new(attrs).run
  end

  def initialize(attrs={})
    @post = Post.new(attrs)
  end

  def run
    if @post.save
      make_http_request
      return true
    else
      return false
    end
  end

  def make_http_request
    # ...
  end
end

Таким образом, PostCreator.run(attrs) является де-факто способом создания поста вместо прохождения через Пост. Теперь для проверки сохранений в Post нет необходимости отключать обратные вызовы. Если вы хотите протестировать процесс PostCreator, никакой магии не происходит, вы можете легко заглушить любые методы или протестировать их независимо. (Вы можете утверждать, что уничтожение методов здесь - это то же самое, что и блокирование обратных вызовов AR, но я думаю, что это более ясно, что происходит.) Очевидно, что это обрабатывает только пост-создание, но вы могли бы сделать то же самое и для пост-обновления.

Во всяком случае, разные идеи, выберите свой яд.

0 голосов
/ 15 мая 2012

Метод #set_some_values ​​здесь вызывается, когда вы вызываете #save для записи.Так что это не имеет ничего общего с конструктором, и поэтому вам не нужно заглушки User.any_instance - просто сделайте свою запись, а затем выполните частичную заглушку, как в:

record.stub(:set_some_values)
record.save
...