Рубин: Rspec двойная путаница - PullRequest
0 голосов
/ 15 декабря 2018

Неудачный тест заставляет меня задуматься о том, что такое двойной тест (каркас теста - RSpec).

Из моего понимания:

Макет - это фальшивый объект, который заменяет сотрудника тестируемого предмета

Итак, допустим, что яиметь Person класс:

class Person 

  def default_number_of_products 
    Product.new 
  end

end

и a Product класс:

class Product
  def initialize 
    @default_number = 3
  end
end

Чтобы проверить, что вызов default_number_of_products для Person приводит к продуктуПолучив new, я пишу тест, который выглядит следующим образом:

RSpec.describe Person do 
  let(:person) { Person.new }

  describe '#default_number_of_products' do     
    it 'invokes new on product' do 
      product = double(Product)
      expect(product).to receive(:new)
      person.default_number_of_products
    end
  end
end

, который завершается ошибкой и возвращает эту ошибку:

// ♥ rspec spec/lib/person_spec.rb 
F

Failures:

  1) Person#default_number_of_products invokes new on product
     Failure/Error: expect(product).to receive(:new)

       (Double Product).new(*(any args))
           expected: 1 time with any arguments
           received: 0 times with any arguments
     # ./spec/lib/person_spec.rb:23:in `block (3 levels) in <top (required)>'

Finished in 0.01006 seconds (files took 0.1052 seconds to load)
1 example, 1 failure

Failed examples:

rspec ./spec/lib/person_spec.rb:21 # Person#default_number_of_products invokes new on product

С другой стороны, тест ниже проходит:

RSpec.describe Person do 
  let(:person) { Person.new }
  let(:product) { Product }

  describe '#default_number_of_products' do   
    it 'invokes new on product' do  
      expect(product).to receive(:new)
      person.default_number_of_products
    end
  end
end

Вопрос :

  • Я думал, что можно удалить зависимость от объекта сотрудничества с помощью двойного - таким образом, тесты могут быть написаны безнеобходимость реализации сотрудничающего класса.Приведенные выше результаты указывают на то, что мое понимание не совсем верно.Что мне не хватает, пожалуйста?

Ответы [ 2 ]

0 голосов
/ 15 декабря 2018

double - это просто объект, который вы создаете для взаимодействия с тестом, поскольку вы действуете как «класс», который вы можете использовать class_double.Когда вы пишете product = double(Product), он создает тестовую переменную product, но не заменяет существующий класс Product.

из: https://relishapp.com/rspec/rspec-mocks/docs/verifying-doubles/using-a-class-double

предоставляется class_doubleв качестве дополнения к instance_double с той разницей, что он проверяет методы класса в данном классе, а не методы экземпляра.

Кроме того, он также предоставляет удобный метод as_stubbed_const для замены конкретных классов определенным двойным

Это будет работать:

it 'invokes new on product' do 
  product = class_double(Product).as_stubbed_const
  expect(product).to receive(:new)
  person.default_number_of_products
end
0 голосов
/ 15 декабря 2018

Ты рядом.Вот как смоделировать этот вызов Person.new:

Сначала давайте сделаем классы Person и Product немного более реалистичными ...

class Person 
  def default_number_of_products 
    Product.new.default_number
  end
end

class Product
  attr_reader :default_number

  def initialize
    @default_number = 3
  end
end

Теперь для Person тестов, которые высмеивают класс Product ...

describe Person do
  subject(:person) { described_class.new }

  describe '#default_number_of_products' do
    let(:product) { instance_double(Product, default_number: 42) }

    before do
      allow(Product).to receive(:new).and_return(product)
    end

    it 'returns 42' do
      expect(person.default_number_of_products).to eq(42)
    end
  end
end

Если бы код в Product был выполнен, вызов person.default_number_of_products возвратил бы 3.Вместо этого этот тест шпионил за Product.new и возвращал двойное вместо реального Product.Итак, когда код в person.default_number_of_products выполняется, он видит двойное число, которое имеет default_number, равное 42.

Наконец, в своем вопросе выше вы упомянули, что думали, что mocks должны позволить вам создать одинКласс без необходимости создавать коллаборационистов.Это верное утверждение.Однако в приведенных выше тестах instance_double(Product, ...) фактически создает тип утки из реального класса Product.Таким образом, это должно быть определено.Если вы хотите использовать TDD до создания класса Product, вы можете передать строку вместо имени класса, например:

let(:product) { instance_double('product', default_number: 42) }

HTH

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...