Учебники

RSpec — Test Doubles

В этой главе мы обсудим RSpec Doubles, также известный как RSpec Mocks. Двойник — это объект, который может «стоять» за другим объектом. Вам, наверное, интересно, что именно это означает и зачем вам это нужно.

Допустим, вы создаете приложение для школы, и у вас есть класс, представляющий класс учащихся, и другой класс для учащихся, то есть у вас есть класс и класс ученика. Сначала вам нужно написать код для одного из классов, так что давайте начнем с класса Classroom —

class ClassRoom 
   def initialize(students) 
      @students = students 
   end 
   
   def list_student_names 
      @students.map(&:name).join(',') 
   end 
end

Это простой класс, у него есть один метод list_student_names, который возвращает разделенную запятыми строку имен учеников. Теперь мы хотим создать тесты для этого класса, но как нам это сделать, если мы еще не создали класс Student? Нам нужен тест Double.

Кроме того, если у нас есть «фиктивный» класс, который ведет себя как объект Student, то наши тесты ClassRoom не будут зависеть от класса Student. Мы называем это тестом изоляции.

Если наши тесты ClassRoom не полагаются на какие-либо другие классы, то, когда тест не пройден, мы сразу можем знать, что в нашем классе ClassRoom есть ошибка, а не какой-то другой класс. Имейте в виду, что в реальном мире вы можете создавать класс, который должен взаимодействовать с другим классом, написанным кем-то другим.

Именно здесь RSpec Doubles (mocks) становятся полезными. Наш метод list_student_names вызывает метод name для каждого объекта Student в его переменной-члене @students. Поэтому нам нужен Double, который реализует метод имени.

Вот код для ClassRoom вместе с примером RSpec (test), но обратите внимание, что класс Student не определен —

class ClassRoom 
   def initialize(students) 
      @students = students 
   end
   
   def list_student_names 
      @students.map(&:name).join(',') 
   end 
end

describe ClassRoom do 
   it 'the list_student_names method should work correctly' do 
      student1 = double('student') 
      student2 = double('student') 
      
      allow(student1).to receive(:name) { 'John Smith'} 
      allow(student2).to receive(:name) { 'Jill Smith'} 
      
      cr = ClassRoom.new [student1,student2]
      expect(cr.list_student_names).to eq('John Smith,Jill Smith') 
   end 
end

Когда приведенный выше код будет выполнен, он выдаст следующий вывод. Прошедшее время может немного отличаться на вашем компьютере —

. 
Finished in 0.01 seconds (files took 0.11201 seconds to load) 
1 example, 0 failures

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