Тесты RSpec для начинающих

Использование шаблонов для упрощения написания тестов для вашего приложения на Rails

Первые несколько раз, когда я подошел к ним, специальные тесты казались мне довольно сложными, но через некоторое время возникли определенные закономерности, и все стало становиться на свои места. В этой статье я хочу пройтись по этим шаблонам и предоставить другим начинающим программистам шаблон для написания тестов качества для их приложений Rails. В этой статье я расскажу о наборе тестов RSpec и синтаксисе, но концепция распознавания шаблонов должна быть перенесена на любой другой набор тестов, который вы пытаетесь изучить.

НАСТРОЙКА RSpec для рельсов

Мы рассмотрим добавление драгоценного камня в приложение здесь, но если вы уже сделали это и просто хотите узнать, как писать тесты, не стесняйтесь пропустить. Во-первых, давайте проведем различие между двумя доступными жемчужинами: «rspec» и «rspec-rails». Урезанный «rspec» необходим для запуска этого набора тестов в любом приложении Ruby, в то время как «rspec-rails» добавляет возможность писать спецификации, которые зависят от структуры Rails.

Поскольку в этой статье используется Rails, мы продолжим работу с гемом rspec-rails, но большая часть базовой структуры осталась прежней. Для более глубокого сравнения изучите их соответствующие репозитории здесь: RSpec | RSpec-Rails.

Предполагая, что у вас уже сгенерировано приложение Rails, начните включать RSpec-Rails, добавив гем в свой Gemfile:

group :development, :test do
  gem 'rspec-rails', '~> 3.8’
end

(Согласно репозиторию rspec-rails, версия 3.8 является самой последней на дату публикации.) Не забудьте запустить «пакетную установку» со своего терминала.

Затем создайте стандартные файлы конфигурации для гема RSpec-Rails с помощью следующей команды:

rails generate rspec:install

Это добавит в ваше приложение папку «spec» с файлами «spec_helper.rb» и «rails_helper.rb». (В качестве альтернативы, более урезанная версия RSpec имеет только файл spec_helper.rb.)

Теперь мы хотим настроить наши файлы спецификаций так, чтобы они совпадали с моделями / представлениями / контроллерами, которые мы создаем в нашем приложении. Для каждого компонента папки приложения, для которого мы хотим написать тесты, мы можем создать соответствующий файл в папке «spec», которую мы только что сгенерировали в соответствии с соглашениями Rails. Например, имея модель пользователя в «app / models / user.rb», я хочу, чтобы пользовательская спецификация находилась в «spec / models / user_spec.rb».

Первый способ добиться этого - записать файлы вручную, но, к счастью, Rails поставляется с некоторыми генераторами, которые позаботятся об этом за нас. Если вы еще не настроили соответствующий ресурс, но правильно установили гем rspec-rails, запуск генератора Rails для ресурса будет включать связанный с ним файл спецификации. Для примера модели пользователя:

rails generate model user  --(or)--  rails g model user

АЛЬТЕРНАТИВНО, если ресурс уже был добавлен в приложение, есть еще несколько вариантов для создания тестового файла. Выполните указанную выше команду и добавьте флаг «- skip», который будет пропускать существующие файлы во избежание конфликтов. Или используйте команды генерации, предоставленные RSpec:

rails generate rspec:model user --(or)-- rails g rspec:model user

Это должно создать файл «user_spec.rb», содержащий следующий шаблон:

require 'rails_helper'
RSpec.describe User, type: :model do
  pending "add some examples to (or delete) #{__FILE__}"
end

Создайте столько файлов, сколько вам нужно для ваших ресурсов. (В качестве примечания, это обычная практика, когда тесты для файлов «просмотра» включаются в спецификации «функций». Фактически, я буду использовать это соглашение в этой статье.) Для файла спецификации на существующем контроллере пользователей, команда выглядит так:

rails generate rspec:controller users

и вы можете найти полный список доступных генераторов, набрав:

rails generate --help | grep rspec

НАПИСАНИЕ ТЕСТОВ

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

Возвращаясь к сгенерированной нами спецификации модели пользователя, давайте добавим наш первый тестовый код, чтобы установить шаблон, которому мы хотим следовать.

В этом файле у нас уже есть блок «делать», который устанавливает, что мы описываем модель пользователя; все тесты в этом блоке должны пытаться делать именно это. Первое, что мы добавим, - это внутренний блок «делать», который описывает аспект модели, который мы хотим протестировать. На самом фундаментальном уровне мы хотим быть уверены, что наше приложение действительно успешно создаст экземпляр нашей модели. Итак, мы хотим установить, что мы пишем спецификации, которые проверяют, можно ли создать пользователя, добавив

RSpec.describe User, type: :model do
  describe 'creation' do
    
  end
end

в блоке User Model. Внутри этого внутреннего блока каждый тест, описывающий создание пользователя, будет использовать следующий трехэтапный шаблон:

  1. Тест будет описывать, что он должен делать, создавая третий блок «делать».
RSpec.describe User, type: :model do
  describe 'creation' do
    it 'can be created' do
    end
  end
end

Строка между «it» и «do» не требует особого синтаксиса, но вы должны быть ясными и описательными; это сообщение на простом английском языке для тех, кто будет проводить тест.

2. Тест запускает тестируемую функциональность. Это может быть операция CRUD для экземпляра ресурса, переход на страницу, проверка атрибутов / входных данных, проверка наличия определенных элементов на странице, проверка определенного несанкционированного / нежелательного поведения и т. Д. Здесь, чтобы проверить Пользовательская модель, нам нужен наш набор тестов для инициализации экземпляра этой модели:

RSpec.describe User, type: :model do
  describe 'creation' do
    it 'can be created' do
      user = User.create(username: "testuser", password: "asdf", password_confirmation: "asdf")
      
    end
  end
end

Экземпляр должен быть инициализирован с любыми параметрами, необходимыми для прохождения теста. Если атрибуты еще не требуются, простая команда «user = User.create» пока будет работать, но когда добавлены требования / проверки, этот тест не пройдет без них. Мой класс User уже требует имени пользователя, пароля и подтверждения пароля, поэтому у моего экземпляра Test User они также должны быть.

3. Тест объявляет ожидаемое поведение, которое он проверяет. Это наиболее важное утверждение в тесте, поскольку оно точно определяет, чего мы ожидаем. Чтобы сделать это утверждение ясным и легким для поиска, знайте, что оно всегда начинается с ключевого слова «ожидать».

RSpec.describe User, type: :model do
  describe 'creation' do
    it 'can be created' do
      user = User.create(username: "testuser", password: "asdf", password_confirmation: "asdf")
      
      expect(user).to be_valid
    end
  end
end

Метод «expect» принимает в качестве аргумента объект, на который мы смотрим, чтобы продемонстрировать желаемое поведение. Для модели обычно это сам экземпляр или один из его атрибутов. Для описания навигации или отображения элементов мы часто используем в качестве аргумента «page», «current_path» или «response». «.To» прикрепляется к методу «expect» и идет сразу после аргумента (ов). Чтобы проверить обратное поведение, используйте вместо этого ".to_not".

Затем метод «expect» будет искать сопоставитель, метод ключевого слова, который может работать отдельно или принимать собственные аргументы и определяет ожидаемый результат. Полный список встроенных сопоставителей слишком велик для этой статьи, но некоторые из наиболее распространенных - это 'be_valid', 'eq (некоторое значение)' (сокращение от ' равно '),' have_content (содержимое в виде строки или регулярного выражения) ', &' изменить (некоторое значение) .by (amt) '.

ТЕСТ КОНТРОЛЛЕРА ДЛЯ СРАВНЕНИЯ

Теперь мы хотим написать простой тест, чтобы проверить правильность работы нашего пользовательского контроллера, в частности действие #create. Возможно, этот тест является избыточным, поскольку он снова проверяет, может ли пользователь быть успешно создан, но он действительно гарантирует, что Контроллер настроен правильно, а также Модель. Кроме того, в этой спецификации мы увидим немного другой синтаксис.

Сначала сгенерируйте файл спецификации пользовательского контроллера, используя команду, упомянутую выше в настройке:

rails generate rspec:controller users

В этом файле должен быть внешний блок «do», объявляющий UsersController в качестве нашей цели. В этом блоке давайте определим наше действие #create как фокус этого теста:

RSpec.describe UsersController, type: :controller do
  describe 'create' do
    
  end
end

затем следуйте нашему трехэтапному шаблону, чтобы написать сам тест:

  1. Опишите, что будет делать тест, на простом английском языке.
RSpec.describe UsersController, type: :controller do
  describe 'create' do
    it 'successfully creates a new user' do
    
    end
  end
end

2. Инициируйте функциональность, которую мы ищем, создав экземпляр тестового пользователя:

RSpec.describe UsersController, type: :controller do
  describe 'create' do
    it 'successfully creates a new user' do
      user = User.create(username: "testuser", password: "asdf", password_confirmation: "asdf")
      
    end
  end
end

3. Объявите конкретное поведение, которое мы ожидаем от этой функции:

RSpec.describe UsersController, type: :controller do
  describe 'create' do
    it 'successfully creates a new user' do
      user = User.create(username: "testuser", name: "Test User", password: "asdf", password_confirmation: "asdf")
      
      expect(User.last.username).to eq("testuser")
      
    end
  end
end

И у нас есть рабочий тест контроллера, который гарантирует, что действие #create нашего пользовательского контроллера успешно отправит запрос в нашу базу данных, чтобы добавить нашего тестового пользователя в нашу таблицу пользователей. Одна альтернатива этому синтаксису объединяет шаги 2 и 3 в один оператор, но по-прежнему следует нашему шаблону:

RSpec.describe UsersController, type: :controller do
  describe 'create' do
    it 'successfully creates a new user' do
      expect{
          post :create, params: { :user => { :username => "testuser", :password => "asdf", :password_confirmation => "asdf" } }
        }.to change(User,:count).by(1)
    end
  end
end

РЕЗЮМЕ

Эта статья стала немного длинной, чтобы вдаваться в подробности, но мы лишь поверхностно касаемся написания наборов тестов качества, поэтому я продолжу этот разговор как серию, чтобы мы могли перейти к дополнительным примерам, чтобы прояснить некоторые концепции. , охватите еще несколько расширенных тестов и рефакторов, а также представьте драгоценный камень для генерации тестовых данных без необходимости касаться базы данных.

А пока вот еще один пример, который поможет повторить базовый тестовый шаблон, используемый в RSpec. Если я хочу написать простую спецификацию навигации, чтобы проверить, что я могу успешно перейти на страницу профиля моего тестового пользователя, у меня будет папка «spec / features» с файлом «user_spec.rb», содержащим следующий код:

require 'rails_helper'
RSpec.describe "Users", type: :feature do
  describe "show page" do
    it 'can be reached successfully'  do
      test_user = User.create(username: "testuser", name: "Test User", password: "asdf", password_confirmation: "asdf")
      visit user_path(test_user)
expect(page.status_code).to eq(200)
    end
  end
end

Мои два внешних блока «делать» устанавливают, что я описываю спецификации для страницы «Показать» моего пользователя. Сам тест выполняет 3 функции:

  1. Он четко описывает на простом английском языке, что нужно, что мы можем успешно перейти на страницу шоу, и формирует третий блок «do» для содержимого теста.
  2. Он инициирует тестируемое поведение / функциональность. Во-первых, у нас должен быть экземпляр пользователя для просмотра его страницы показа, затем мы используем метод «посещения» из RSpec, чтобы инициировать навигацию по (надеюсь) действующему маршруту.
  3. Метод «expect (). To» объявляет конкретное поведение, которое мы хотим проверить. Если мы сможем перейти на нужную страницу, она вернет код состояния «200», поэтому мы просто проверяем, чтобы «page.status_code» возвращался с этим значением.

Каждый из наших тестов был создан по следующему шаблону:

RSpec.describe "<Model/Controller>", type: <:type> do
  describe "<what/where>" do
    
      it '<expected behavior in plain-English>'  do
      
      <initialize functionality (seeding an instance, navigating to a page, filling in form inputs, etc) >
      expect(<resource>).to <matcher_method>(do something specific)
    
    end
  end
end

В качестве последнего полезного примечания, чтобы просмотреть расположение и описание каждого теста, который вы создали, выполните следующую команду:

rspec --format documentation

Три примера здесь задокументированы следующим образом:

UsersController
  create
    successfully creates a new user
Users
  show
    can be reached successfully
User
  creation
    can be created

что может стать весьма кстати, когда вы начнете писать много-много тестов.

Доступно несколько вариантов методов и множество различных способов инициализации и тестирования функциональности вашего приложения. Представленные здесь примеры, конечно, не ЕДИНСТВЕННЫЙ способ написать тест. В этой статье было не так много конкретных методов или примеров, но, надеюсь, этот общий обзор имеет некоторый смысл и задает контекст для создания ваших собственных тестов. Если да, то в следующий раз я попытаюсь представить более широкий спектр тестовых примеров с немного менее многословным объяснением каждого из них. А пока желаю удачного кодирования!