代码之家  ›  专栏  ›  技术社区  ›  Matilda Smeds Phrogz

如果没有任何实例,如何断言没有进行方法调用?

  •  0
  • Matilda Smeds Phrogz  · 技术社区  · 6 年前

    我有一门课,在某种情况下 :my_method ,但在另一种情况下不能调用方法 :我的方法 :我的方法 不应调用。

    Using any_instance is generally discouraged ,所以我很乐意学习一种很好的方法来替换它。

    这个代码片段是我想写的测试的简化示例。

    class TestSubject
      def call
        call_me
      end
    
       def call_me; end
       def never_mind; end
    end
    
    require 'rspec'
    
    spec = RSpec.describe 'TestSubject' do
      describe '#call' do
        it 'calls #call_me' do
          expect_any_instance_of(TestSubject).to receive(:call_me)
          TestSubject.new.call
        end
    
        it 'does not call #never_mind' do
          expect_any_instance_of(TestSubject).not_to receive(:never_mind)
          TestSubject.new.call
        end
      end
    end
    
    spec.run # => true
    

    它工作,但使用 expect_any_instance_of

    如何更换?

    3 回复  |  直到 6 年前
        1
  •  4
  •   kevcha    6 年前

    我会做那样的事

    describe TestSubject do
      describe '#call' do
        it 'does not call #something' do 
          subject = TestSubject.new
          allow(subject).to receive(:something)
    
          subject.call
    
          expect(subject).not_to have_received(:something)
        end
      end
    end
    

    希望这有帮助!

        2
  •  1
  •   Jay-Ar Polidario    6 年前

    我通常是这样进行单元测试的。我更新了代码以支持您(或其他读者)将来可能遇到的其他问题。

    class TestSubject
      def call
        some_call_me_value = call_me
        call_you(some_call_me_value)
      end
    
      def call_me; end
      def call_you(x); end
      def never_mind; end
    
      class << self
        def some_class_method_a; end
    
        def some_class_method_b(x, y); end
      end
    end
    
    require 'rspec'
    
    spec = RSpec.describe TestSubject do
      context 'instance methods' do
        let(:test_subject) { TestSubject.new }
    
        describe '#call' do
          let(:args) { nil }
          let(:mocked_call_me_return_value) { 'somecallmevalue' }
          subject { test_subject.call(*args) }
    
          before do
            allow(test_subject).to receive(:call_me) do
              mocked_call_me_return_value
            end
          end
    
          it 'calls #call_me' do
            expect(test_subject).to receive(:call_me).once
            subject
          end
    
          it 'calls #call_you with call_me value as the argument' do
            expect(test_subject).to receive(:call_you).once.with(mocked_call_me_return_value)
            subject
          end
    
          it 'does not call #never_mind' do
            expect(test_subject).to_not receive(:never_mind)
            subject
          end
    
          it 'calls in order' do
            expect(test_subject).to receive(:call_me).once.ordered
            expect(test_subject).to receive(:call_you).once.ordered
            subject
          end
        end
    
        describe '#call_me' do
          let(:args) { nil }
          subject { test_subject.call_me(*args) }
    
          # it ...
        end
    
        describe '#call_you' do
          let(:args) { nil }
          subject { test_subject.call_you(*args) }
    
          shared_examples_for 'shared #call_you behaviours' do
            it 'calls your phone number'
            it 'creates a Conversation record'
          end
    
          # just an example of argument-dependent behaviour spec
          context 'when argument is true' do 
            let(:args) { [true] }
    
            it 'does something magical'
            it_behaves_like 'shared #call_you behaviours'
          end
    
          # just an example of argument-dependent behaviour spec
          context 'when argument is false' do
            let(:args) { [false] }
    
            it 'does something explosive'
            it_behaves_like 'shared #call_you behaviours'
          end
        end
      end
    
      context 'class methods' do
        let(:args) { nil }
    
        describe '#some_class_method_a' do
          let(:args) { nil }
          subject { TestSubject.some_class_method_a(*args) }
    
          # it ...
        end
    
        describe '#some_class_method_b' do
          let(:args) { [1, 2] }
          subject { TestSubject.some_class_method_b(*args) }
    
          # it ...
        end
      end
    end
    
    spec.run # => true
    
        3
  •  0
  •   Fabio    6 年前


    这将使您的测试紧跟实现细节,并将迫使您在每次重构(在不改变行为的情况下改变实现细节)您的被测类时都改变测试。

    而是针对返回值或更改的应用程序状态进行测试。
    很难想出这个例子,你没有提供足够的上下文关于测试中的类。

    class CreateEntity
      def initialize(name)
        @name = name
      end 
    
      def call
        if company_name?(@name)
          create_company
        else
          create_person
        end
      end
    
      def create_person
        Person.create!(:name => @name)
      end
    
      def create_company
        Company.create!(:name => @name)
      end
    end
    
    # tests
    
    RSpec.describe CreateEntity do
      let(:create) { CreateEntity.new(name).call }
    
      describe '#call' do
        context 'when person name is given' do
          let(:name) { 'Firstname Lastname' }
          it 'creates a person' do
            expect { create }.to change { Person.count }.by(1) 
          end
    
          it 'do not create a company' do
            expect { create }.not_to change { Company.count }
          end
        end
    
        context 'when company name is given' do
          let(:name) { 'Name & Sons Ltd' }
          it 'creates a company' do
            expect { create }.to change { Company.count }.by(1) 
          end
    
          it 'do not create a person' do
            expect { create }.not_to change { Person.count }
          end
        end
      end
    end
    

    通过以上测试,我将能够改变 CreateEntity.call