Краткий этот док, как ясно уже из названия статьи, посвящен описанию процедуры тестирования формы отправки сообщений, опубликованной на одной из страничек вашего Ruby on Rails приложения; и предназначен как для новичков в деле программирования, так и для более зрелых их коллег, для которых навыки написания тестов не являются, назовем это так, сильной стороной (подобное случается, и нередко).
Вы в любой момент можете клонировать репозиторий CRUD-Blog и самостоятельно поэкспериментировать с кодом.
Процесс написания формы, которую мы с вами сегодня собрались тестировать, более-менее подробно описан в этих - тынц и тынц - материалах, с которых, возможно, и следует начать предлагаемый экскурс. Здесь же исходим из того, что формочка у вас уже имеет место быть и, идя наперекор канве TDD, необходимо написать тесты для нее (довольно распространенный, к слову сказать, в среде программистов подход).
Итак. Знакомство с RSpec - rspec-rails brings the RSpec testing framework to Ruby on Rails as a drop-in alternative to its default testing framework, Minitest - логично начать с инсталляции, добавив джем в :development и :test группы Gemfile:
# Gemfile
group :development, :test do
gem 'rspec-rails', '~> 5.0.0'
end
# Download and install
$ bundle install
$ rails generate rspec:install
И далее, уже непосредственно для описываемых тестов, я запускаю еще две (эти модель и мэйлер, подчеркну, уже присутствуют в моем приложении) генерации:
$ rails g rspec:model contact
$ rails g rspec:mailer form
ОК. Файл модели Contact крайне несложен, содержит всего несколько валидаций формы:
app/models/contact.rb
class Contact < ApplicationRecord
validates :name, length: { minimum: 3 }
validates :email, format: { with: /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i, on: :create }
validates :subject, length: { minimum: 5 }
validates :message, length: { minimum: 15 }
end
Соответственно, спек способен выглядеть (всегда возможны, разумеется, варианты) так:
spec/models/contact_spec.rb
require 'rails_helper'
RSpec.describe Contact, type: :model do
subject(:contact) do
Contact.new(
name: 'test',
email: Адрес электронной почты защищен от спам-ботов. Для просмотра адреса в браузере должен быть включен Javascript. ',
subject: 'something',
message: 'Hi how are you doing?'
)
end
before { contact.valid? }
it 'requires an email' do
expect(contact).to be_valid
# other test on error message here perhaps?
end
it { is_expected.to validate_length_of(:name).is_at_least(3) }
it { is_expected.to validate_length_of(:subject).is_at_least(5) }
it { is_expected.to validate_length_of(:message).is_at_least(15) }
end
Запускаем тест, и он проходит:
bundle exec rspec spec/models/contact_spec.rb
....
Finished in 0.12443 seconds (files took 9.13 seconds to load)
4 examples, 0 failures
Контроллер contacts содержит, помимо прочего, следующий метод, передавая мэйлеру (либо не передавая) экземпляр класса Contact, т.е. параметры имя отправителя, его email, тему сообщения и, собственно, сам мессадж:
def create
@contact = Contact.new(contact_params)
if verify_recaptcha
respond_to do |format|
if @contact.save
FormMailer.new_form_email(@contact).deliver_now
format.js { flash.now[:success] = 'Thank you for your message.' }
else
format.js { flash.now[:error] = see_errors(@contact) }
end
end
end
end
app/mailers/form_mailer.rb
class FormMailer < ApplicationMailer
def new_form_email(x)
@form = x
mail(to: ENV['SEND_EMAIL_TO'], subject: ENV['SUBJECT_EMAIL'])
end
end
Параметры отправки записаны в переменных окружения (environment variable); для этой цели я использую, в данном случае, gem 'figaro', хотя вы можете воспользоваться любым из доброго десятка иных способов сделать все то же самое:
config/application.yml
SEND_EMAIL_TO: Адрес электронной почты защищен от спам-ботов. Для просмотра адреса в браузере должен быть включен Javascript. '
SUBJECT_EMAIL: 'Hurray! You got a new form!'
Стало быть, наши тесты мэйлера будут выглядеть следующим образом:
spec/mailers/form_spec.rb
require 'rails_helper'
RSpec.describe FormMailer, type: :mailer do
before(:each) do
ActionMailer::Base.delivery_method = :test
ActionMailer::Base.perform_deliveries = true
ActionMailer::Base.deliveries = []
end
let(:form) do
Contact.create!(
name: 'test',
email: Адрес электронной почты защищен от спам-ботов. Для просмотра адреса в браузере должен быть включен Javascript. ',
subject: 'subject',
message: 'Bluh bluh bluh.'
)
end
after(:each) do
ActionMailer::Base.deliveries.clear
end
it 'should send an email' do
FormMailer.new_form_email(form).deliver_now
expect(ActionMailer::Base.deliveries.count).to eq(1)
end
it 'sends to the correct receiver' do
FormMailer.new_form_email(form).deliver_now
expect(ActionMailer::Base.deliveries.first.to.first).to eq(Адрес электронной почты защищен от спам-ботов. Для просмотра адреса в браузере должен быть включен Javascript. ')
end
it 'should set the subject correctly' do
FormMailer.new_form_email(form).deliver_now
expect(ActionMailer::Base.deliveries.first.subject).to eq('Hurray! You got a new form!')
end
end
Словом, три теста: для начала проверяем, собственно, сам процесс отправки, далее email администратора сайта и тему сообщения. Вкупе с описанными выше валидациями модели можем считать, что на данном этапе тесты вполне неплохо покрывают логику отправку сообщений с сайта... впрочем, сделаем для очистки совести еще и несложный тест контроллера, убедившись, что сервер успешно отдает страничку:
rails g rspec:controller contacts
spec/requests/contacts_spec.rb
require 'rails_helper'
RSpec.describe 'Contacts', type: :request do
describe 'GET /contacts/index' do
before do
get '/contacts/index'
end
it 'returns http success' do
expect(response).to have_http_status(:success)
end
end
end
Пожалуй, на сегодня это все, что хотелось рассказать... но продолжение, вполне возможно, последует.