- Что это
- Установка и генерация конфигов
- Пишем первый тест
- Вникаем в поведение
- Пишем код
- И исправляем ошибки
Кратко
RSpec — инструмент тестирования предметно-ориентированного типа, на основе так называемого доменно-специфичного подхода/языка (DSL). RSpec создавался для тестирования кода на языке Ruby.
Идея состояла в том, чтобы сначала писать тесты, а затем писать код, достаточно корректный чтобы пройти тесты, а затем приступать к рефакторингу кода (на чем строится подобный BDD-подход, вкратце рассказываем здесь).
Поскольку RSpec является инструментом тестирования, изначально ориентированным на поведенческую разработку (еще одно альтернативное название BDD), он фокусируется на описании того, что делает приложение, и на том, следует ли оно спецификации.
RSpec создавался так, чтобы его было легко читать. Использование BDD-подхода покачивает скиллы не только разработчика но и тестировщика, поскольку заставляет вникать в логику программы и ее поведение. Сейчас посмотрим, как начать писать BDD-тесты в RSpec.
Установка и генерация конфигов
Для начала убедимся, что RSpec установлен (если еще нет). Это очень просто, достаточно установить гем RSpec с помощью команды:
gem install rspec
Теперь у нас установлен rspec
, и мы можем генерировать конфигурационные файлы. Сначала создадим каталог и перейдем в него. Затем выполним команду, которая создаст конфигурационные файлы («конфиги»):
mkdir rspec_example cd rspec_example/ rspec --init
Эта команда создаст два файла, первый из которых находится в папке spec
и называется spec_helper.rb
, а второй — .rspec
. В этой папке spec
и будем писать наши тесты.
Начнем с простого примера. Попробуем сделать простейшую программу-приветствие, которая будет принимать введенное вами имя и приветствовать вас. Сначала создадим ruby-файл в главном каталоге и файл greeting_spec
в папке spec
:
touch greeting.rb touch spec/greeting_spec.rb
В файле greeting.rb создадим класс greeting
, который будет инициализирован с именем:
class Greeting def initialize(name) @name = name end end
Пишем первый тест
Теперь, следуя нашим BDD-принципам, сначала напишем тест, имплементирующий (реализующий) приветствие. Мы хотим, чтобы класс Greeting
содержал метод, который при его вызове приветствовал пользователя (выводил приветствие). Начнем создавать «скелет теста» для нашего greeting_spec.rb
.
require './greeting' RSpec.describe Greeting do end
Сначала нам нужен './greeting'
. В этом файле теста мы описываем поведение класса. Чтобы RSpec знал, с каким классом мы сейчас работаем, мы должны «обернуть» определение теста блоком RSpec.describe
.
Важно знать, что имя класса не является обязательным, и при желании, может быть заменено строкой. Слово describe
является ключевым словом в RSpec и используется для описания “Example Group
” (Группы Примеров), которую можно считать набором тестов (или коллекцией). Для нашей программы нам нужен метод, который будет выдавать приветствие с указанием введенного имени.
Вникаем в поведение
А теперь можем приступить к описанию метода.
require './greeting' RSpec.describe Greeting do describe '#greet_me' do it 'prints a greeting with the name' do end end end
Ключевое слово it
используется для определения «Example
«, который и является нашим тестом. Как и describe
, оно принимает имя класса или строковый аргумент, и должно использоваться с аргументом блока. Внутри этого блока мы и будем тестировать поведение нашей программы.
Пишем код
Мы хотим создать новый экземпляр класса Greeting
и вызвать метод greet_me
. Затем мы будем ожидать, что он выведет приветствие. Теперь наш тест будет выглядеть следующим образом:
require './greeting' RSpec.describe Greeting do describe '#greet_me' do it 'prints a greeting with the name' do greeting = Greeting.new('Ray') expect(greeting.greet_me).to eq('Hello Ray') end end end
Ключевое слово expect
в RSpec используется для определения ‘Expectation
‘. На этом шаге мы проверяем, было ли выполнено определенное ожидание. Метод .eq
называется матчером. Когда будет вызван метод greet_me
и выходное, полученное значение будет равно ‘Hello Ray
‘ — тест пройдет.
Синтаксис RSpec очень прост для восприятия и почти полностью совпадает с обычным разговорным английским языком. Введите команду rspec
в терминале, и увидите примерно следующее:
rspec Failures: 1) Greeting#greet_me prints a greeting with the name Failure/Error: expect(greeting.greet_me).to eq('Hello Ray') NoMethodError: undefined method `greet_me' for #<Greeting:0x00005639ee5775c8 @name="Ray"> # ./spec/greeting_spec.rb:7:in `block (3 levels) in <top (required)>' Finished in 0.00139 seconds (files took 0.06321 seconds to load) 1 example, 1 failure
А тест не прошел, поскольку у нас не определен метод greet_me
. Поэтому давайте пройдем шаг за шагом и создадим метод с именем greet_me
в файле greeting.rb
.
class Greeting def initialize(name) @name = name end def greet_me end end
Теперь, когда метод уже определен, запустите rspec
еще раз, и увидите, что ошибка выглядит теперь так:
Failures: 1) Greeting#greet_me prints a greeting with the name Failure/Error: expect(greeting.greet_me).to eq('Hello Ray') expected: "Hello Ray" got: nil (compared using ==) # ./spec/greeting_spec.rb:7:in `block (3 levels) in <top (required)>' Finished in 0.01522 seconds (files took 0.06191 seconds to load) 1 example, 1 failure
Ошибка сообщает, что ожидался вывод «Hello Ray
«, но вместо этого получен nil
(то есть «пусто»). На этом можете увидеть, как такой цикл обратной связи в BDD может быть полезен при написании правильного кода.
И исправляем ошибки
Теперь, когда мы знаем, каким должен быть следующий шаг, давайте выполним тест, так чтобы он прошел. Добавим следующее в наш код:
class Greeting def initialize(name) @name = name end def greet_me "Hello #{@name}" end end
Теперь, когда запускаем RSpec, видим, что все тесты успешно прошли!
Finished in 0.00151 seconds (files took 0.06547 seconds to load) 1 example, 0 failures
Вообще же, тесты сами по себе — важная часть документации (и не только в мире Ruby). Например можно создавать команду, которая показывает, за что отвечает тот или иной метод. Таким образом, если кто-то из других членов команды захочет посмотреть, что там с классом Greeting
, он сможет проверить, каково его поведение.
rspec --format documentation Greeting #greet_me prints a greeting with the name Finished in 0.00095 seconds (files took 0.06487 seconds to load) 1 example, 0 failures
BDD-методика тестирования помогает убедиться в том, что написанный код ведет себя именно так, как планировалось. BDD-процесс делает членов DEV и QA-команд более опытными. Кроме того, RSpec упоминается в вакансиях, имеющих отношение к Ruby, так что это ценные скиллы, если планируете освоить Ruby или войти в QA. Успехов вам в написании тестов и удачного кодинга!