1

Имея большой стек моделей и используя методы кэширования куклы широко, каждый заканчивается тем, что многие модели родительских моделей были «тронуты» после обновления модели.Как отключить параметр sort_to: touch в тестах Rspec для моделей Rails?

Во время тестирования это, кажется, время, когда вы пытаетесь проверить эту особенность.

Есть ли способ предотвратить модели touchbelongs_to ассоциации для тестовой среды или на уровне тестирования?

UPDATE 1:

Моя первая попытка случае будет

# /config/initializers/extensions.rb 
# 
class ActiveRecord::Base 
    def self.without_touch_for_association(association_name, &block) 
    association_name = association_name.to_sym 
    association = self.reflect_on_all_associations(:belongs_to).select { |reflection| reflection.name == association_name }.first 
    options = association.options 
    association.instance_variable_set :@options, options.except(:touch) 

    yield 

    association.instance_variable_set :@options, options 
    end 
end 

Post.without_touch_for_association(:user) do 
    Post.last.save 
end 

Конечно, никакого успеха и сохранения Post.last до сих пор трогает это User.

ОБНОВЛЕНИЕ ОБОСНОВАНИЕ:

Я понимаю и согласен, что этот подход может быть источником ошибок, и это не очень хорошая практика вообще. Дело в том, что у меня огромный пакет с множеством интеграционных и модульных тестов. Кубическое кэширование также проникает в дерево моделей. Каждый раз, когда я смотрю журналы, я вижу существенные% запросов касательно. Я знаю, что лучшим способом было бы оптимизировать модульные тесты, чтобы добавить больше насмешек, ступлений и меньшей настойчивости. Решение проблемы в рамках интеграционных тестов сложнее.

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

РЕШЕНИЕ: см. Мой собственный ответ ниже для рабочего кода.

+0

Извините заранее, что не предлагает прямого решения. Тем не менее, я был бы очень осторожен в попытке этого в первую очередь, так как это значительно изменило бы поведение вашего приложения и стало бы богатым потенциальным источником ошибок, связанных с производством. Если вызовы '# touch' действительно вызывают значительное замедление в вашем тестовом наборе, кажется, что в первую очередь создается слишком много сохраняющихся записей. Вас интересуют прежде всего модульные тесты или интеграционные тесты? – SimonC

+0

Спасибо SimonC! Я уточнил вопрос с моим обоснованием. Ура! – dgilperez

+0

Ну, я не думаю, что это очень хорошее предложение, но нельзя использовать метод касания для любого экземпляра, например: 'User.any_instance.stub (: touch) .and_return (true)' поэтому он на самом деле ничего не делает? –

ответ

3

Для Rails> = 4,2

Благодаря @Dorian, в Rails 4.2 можно использовать ActiveRecord::NoTouching.

Для Rails < 4,2

Мой рабочий код в RSpec файле поддержка:

# /spec/support/active_record_extensions.rb 
class ActiveRecord::Base 
    def self.without_touch_for_association(association, &block) 
    method_name = :"belongs_to_touch_after_save_or_destroy_for_#{association}" 
    return unless self.instance_methods.include?(method_name) 

    method = self.send(:instance_method, method_name) 
    self.send(:define_method, method_name) { true } 

    yield 

    self.send(:define_method, method_name, method) 
    nil 
    end 

    def self.disable_touch_associations! 
    associations = self.reflect_on_all_associations(:belongs_to) 
    associations.each do |association| 
     self.without_touch_for_association association.name do 
     return 
     end 
    end 
    nil 
    end 
end 

Добавьте это в ./spec/spec_helper.rb отключить все сенсорные вызовы для любой модели, определенной для целого набора тестов:

RSpec.configure do |config| 
    if ENV['SILENCE_TOUCHES'] 
    config.before :suite do 
     ActiveRecord::Base.descendants.each {|model| model.disable_touch_associations! } 
    end 
    end 
end 

Временное отключение прикосновения для модели и ассоциации в па ритикулярный тест.

Post.without_touch_for_association(:user) do 
    Post.last.save 
end 

Благодаря @xlembouras ниже для указания меня в нужном направлении!

Я играю с этой функцией на наших тестах, и я замечаю 25% -ное снижение скорости набора тестов для 30-минутного набора тестов. Я могу опубликовать более точные результаты после более тщательных исследований.

+1

Это совсем не то, что я говорю. Вы в конечном итоге обезвреживаете свою «ActiveRecord :: Base» и добавляете дополнительную функциональность во весь стек только для тестирования. Все это должно быть извлечено либо на ваши фабрики (желательно), либо до блока в ваших спецификациях, которые будут выполняться только в случае необходимости и для определенных моделей. – xlembouras

+0

Эй! Ты прав. Я на самом деле использовал позднее добавление кода в файл spec_helper.rb, управляемый переменной окружения. Я не смог объяснить это в ответ, но я только что обновил его. Спасибо что подметил это! – dgilperez

+0

Кстати, я бы хотел использовать это на своих заводах, но добавление черты для каждой фабрики не является хорошим подходом ИМХО. Я не нашел, как добавить этот бит глобально ко всем фабрикам. – dgilperez

0

Я не уверен, если это будет работать, но вы можете попробовать следующее:

belongs_to :foo, touch: APP_CONFIG['doll_touch'] 

где APP_CONFIG является параметр приложения, который устанавливается после этого guide.

Итак, в вашей части разработки или разработки вы установили doll_touch в true и в своем тесте на false.

+0

Хм, похоже, это должно сработать. Хорошая попытка на деле. Но это потребует изменения исходного кода несколько десятков раз ... Я все равно хотел бы понять, как обезьяна патнет тестовую среду, я буду ждать, пока кто-то ответит на эту часть. Если нет, щедрость ваша. – dgilperez

+0

Не беспокойтесь, я верю, что вы найдете решение «обезьяны заплачено» рано или поздно. –

3

Я не согласен с понятием изменения кода для целей тестирования. Тестирование с моей точки зрения должно быть независимой процедурой.

Как я вижу, вы должны предоставить путь к вашему набору тестов, чтобы изменить поведение модели только для определенных случаев.

Следующий код

class Book < ActiveRecord::Base 
    belongs_to :author, touch: true 
end 

class Author < ActiveRecord::Base 
    has_many :books 
end 

что ваш случай будет определять метод экземпляра

belongs_to_touch_after_save_or_destroy_for_author

за сценой для Book класса. (спасибо AR http://apidock.com/rails/ActiveRecord/Associations/Builder/BelongsTo/add_touch_callbacks)

Так что в вашем тестовом коде вы можете переопределить этот метод, чтобы сделать что-то другое или ничего вообще!

В моем случае я использую RSpec с FactoryGirl, так что я сделал, чтобы создать специальную заводскую черту для Book класса, который переопределяет belongs_to_touch_after_save_or_destroy_for_author для этого объекта

FactoryGirl.define do 
    factory :book do 
    ... 
    ... 
    end 

    trait :no_touch do 
    before(:create) do |book_no_touch| 
     def book_no_touch.belongs_to_touch_after_save_or_destroy_for_author 
     true 
     end 
    end 
    end 
end 

Таким образом, когда вам нужно проверить что-то где касаясь связанных объектов не имеет значения, вы можете создать объект книги с этой фабрики

book = FactoryGirl.create(:book, :no_touch)

+0

Да! Я пробовал этот фрагмент на консоли: '' Post.send (: define_method,: belongs_to_touch_after_save_or_destroy_for_user) {true} '', и ему удалось отключить обратный вызов. Теперь, для A: любая идея о том, как обобщить это для всех классов AR, как я пытался? – dgilperez

+0

Хмммм, возможно, заплатив фабрику FactoryGirl? – dgilperez

+0

Ну, я в конце концов решил это с вашей помощью. Я опубликовал свое полное рабочее решение и пометил его как решенный, как самый полный, но кредит и щедрость идут к вам за то, что указали мне на метод belongs_to_touch .... Благодаря! – dgilperez

9

Предполагая, что вы находитесь на Rails 4.1.4 и новее:

User.no_touching do 
    Post.last.save 
end 

или даже

ActiveRecord::Base.no_touching do 
    Post.last.save 
end 

См ActiveRecord::NoTouching.