[ruby-on-rails] Factory Girl 및 Rspec에서 콜백 건너 뛰기

테스트하는 동안 일부 경우에만 실행하고 싶은 애프터 생성 콜백으로 모델을 테스트하고 있습니다. 공장에서 콜백을 건너 뛰거나 실행하려면 어떻게해야합니까?

class User < ActiveRecord::Base
  after_create :run_something
  ...
end

공장:

FactoryGirl.define do
  factory :user do
    first_name "Luiz"
    last_name "Branco"
    ...
    # skip callback

    factory :with_run_something do
      # run callback
  end
end



답변

이것이 최선의 해결책인지 확실하지 않지만 다음을 사용하여 성공적으로 달성했습니다.

FactoryGirl.define do
  factory :user do
    first_name "Luiz"
    last_name "Branco"
    #...

    after(:build) { |user| user.class.skip_callback(:create, :after, :run_something) }

    factory :user_with_run_something do
      after(:create) { |user| user.send(:run_something) }
    end
  end
end

콜백없이 실행 :

FactoryGirl.create(:user)

콜백으로 실행 :

FactoryGirl.create(:user_with_run_something)


답변

콜백을 실행하지 않으려면 다음을 수행하십시오.

User.skip_callback(:create, :after, :run_something)
Factory.create(:user)

skip_callback은 실행 후 다른 사양에서 지속되므로 다음과 같은 사항을 고려하십시오.

before do
  User.skip_callback(:create, :after, :run_something)
end

after do
  User.set_callback(:create, :after, :run_something)
end


답변

이러한 솔루션 중 어느 것도 좋지 않습니다. 클래스가 아닌 인스턴스에서 제거해야하는 기능을 제거하여 클래스를 손상시킵니다.

factory :user do
  before(:create){|user| user.define_singleton_method(:send_welcome_email){}}

콜백을 억제하는 대신 콜백의 기능을 억제합니다. 어떤면에서는이 접근 방식이 더 명시 적이기 때문에 더 좋습니다.


답변

다른 사용자를 만들 때 after_save 콜백을 더 재사용 할 수 있도록 @luizbranco의 답변을 개선하고 싶습니다.

FactoryGirl.define do
  factory :user do
    first_name "Luiz"
    last_name "Branco"
    #...

    after(:build) { |user|
      user.class.skip_callback(:create,
                               :after,
                               :run_something1,
                               :run_something2)
    }

    trait :with_after_save_callback do
      after(:build) { |user|
        user.class.set_callback(:create,
                                :after,
                                :run_something1,
                                :run_something2)
      }
    end
  end
end

after_save 콜백없이 실행 :

FactoryGirl.create(:user)

after_save 콜백으로 실행 :

FactoryGirl.create(:user, :with_after_save_callback)

내 테스트에서는 사용 된 메서드가 테스트 예제에서 일반적으로 원하지 않는 추가 항목을 실행하기 때문에 기본적으로 콜백없이 사용자를 생성하는 것을 선호합니다.

———- UPDATE ———— 테스트 스위트에 불일치 문제가 있었기 때문에 skip_callback 사용을 중단했습니다.

대체 솔루션 1 (스텁 및 스텁 해제 사용) :

after(:build) { |user|
  user.class.any_instance.stub(:run_something1)
  user.class.any_instance.stub(:run_something2)
}

trait :with_after_save_callback do
  after(:build) { |user|
    user.class.any_instance.unstub(:run_something1)
    user.class.any_instance.unstub(:run_something2)
  }
end

대체 솔루션 2 (내가 선호하는 접근 방식) :

after(:build) { |user|
  class << user
    def run_something1; true; end
    def run_something2; true; end
  end
}

trait :with_after_save_callback do
  after(:build) { |user|
    class << user
      def run_something1; super; end
      def run_something2; super; end
    end
  }
end


답변

Rails 5- skip_callbackFactoryBot 팩토리에서 건너 뛸 때 인수 오류가 발생합니다.

ArgumentError: After commit callback :whatever_callback has not been defined

rails 5 에서 skip_callback이 인식되지 않는 콜백을 처리하는 방법 이 변경되었습니다 .

ActiveSupport :: Callbacks # skip_callback은 이제 인식 할 수없는 콜백이 제거되면 ArgumentError를 발생시킵니다.

언제 skip_callback 공장에서 호출되면, AR 모델의 실제 콜백이 아직 정의되지 않았습니다.

모든 것을 시도하고 나처럼 머리카락을 뽑았다면 여기에 해결책이 있습니다 (FactoryBot 문제 검색에서 얻었습니다) ( 부품 참고raise: false ).

after(:build) { YourSweetModel.skip_callback(:commit, :after, :whatever_callback, raise: false) }

원하는 다른 전략과 함께 자유롭게 사용하십시오.


답변

이 솔루션은 저에게 효과적이며 Factory 정의에 추가 블록을 추가 할 필요가 없습니다.

user = FactoryGirl.build(:user)
user.send(:create_without_callbacks) # Skip callback

user = FactoryGirl.create(:user)     # Execute callbacks


답변

Rspec 3에서 간단한 스텁이 가장 잘 작동했습니다.

allow(User).to receive_messages(:run_something => nil)