Использование shoulda для рефакторинга тестов rspec на моделях Rails

Узнав о shoulda -сопоставителях , ответив на другой вопрос StackOverflow о тестах доступности атрибутов(и, думая, что они довольно классные ), я решил попробовать рефакторинг тестов модели, которые я сделал в Учебнике по Rails , чтобы сделать их еще более краткими и тщательными. Я сделал это благодаря некоторому вдохновению из документации по модулям Shoulda::Matchers::ActiveRecord. иShoulda::Matchers::ActiveModel, а также этот ответ StackOverflow о структурировании тестов shoulda в моделях. Тем не менее, есть еще несколько вещей, в которых я не уверен, и мне интересно, как можно улучшить эти тесты.

Я буду использовать спецификацию User из руководства по Rails в качестве примера, так как она является наиболее подробной и охватывает множество областей, которые можно было бы улучшить. Следующий пример кода был изменен по сравнению с исходнымпользователь _спец.rb, и заменяет код вниз до строки describe "micropost associations". Спецтесты противuser.rbмодель, а ее фабрика определена вфабрики.рб.

спец/модели/пользователь _спец.rb

# == Schema Information
#
# Table name: users
#
#  id              :integer          not null, primary key
#  name            :string(255)
#  email           :string(255)
#  created_at      :datetime         not null
#  updated_at      :datetime         not null
#  password_digest :string(255)
#  remember_token  :string(255)
#  admin           :boolean          default(FALSE)
#
# Indexes
#
#  index_users_on_email           (email) UNIQUE
#  index_users_on_remember_token  (remember_token)
#

require 'spec_helper'

describe User do

  let(:user) { FactoryGirl.create(:user) }

  subject { user }

  describe "database schema" do
    it { should have_db_column(:id).of_type(:integer)
                             .with_options(null: false) }
    it { should have_db_column(:name).of_type(:string) }
    it { should have_db_column(:email).of_type(:string) }
    it { should have_db_column(:created_at).of_type(:datetime)
                             .with_options(null: false) }
    it { should have_db_column(:updated_at).of_type(:datetime)
                             .with_options(null: false) }
    it { should have_db_column(:password_digest).of_type(:string) }
    it { should have_db_column(:remember_token).of_type(:string) }
    it { should have_db_column(:admin).of_type(:boolean)
                             .with_options(default: false) }
    it { should have_db_index(:email).unique(true) }
    it { should have_db_index(:remember_token) }
  end

  describe "associations" do
    it { should have_many(:microposts).dependent(:destroy) }
    it { should have_many(:relationships).dependent(:destroy) }
    it { should have_many(:followed_users).through(:relationships) }
    it { should have_many(:reverse_relationships).class_name("Relationship")
                        .dependent(:destroy) }
    it { should have_many(:followers).through(:reverse_relationships) }
  end

  describe "model attributes" do
    it { should respond_to(:name) }
    it { should respond_to(:email) }
    it { should respond_to(:password_digest) }
    it { should respond_to(:remember_token) }
    it { should respond_to(:admin) }
    it { should respond_to(:microposts) }
    it { should respond_to(:relationships) }
    it { should respond_to(:followed_users) }
    it { should respond_to(:reverse_relationships) }
    it { should respond_to(:followers) }
  end

  describe "virtual attributes and methods from has_secure_password" do
    it { should respond_to(:password) }
    it { should respond_to(:password_confirmation) }
    it { should respond_to(:authenticate) }
  end

  describe "accessible attributes" do
    it { should_not allow_mass_assignment_of(:password_digest) }
    it { should_not allow_mass_assignment_of(:remember_token) }
    it { should_not allow_mass_assignment_of(:admin) }
  end

  describe "instance methods" do
    it { should respond_to(:feed) }
    it { should respond_to(:following?) }
    it { should respond_to(:follow!) }
    it { should respond_to(:unfollow!) }
  end

  describe "initial state" do
    it { should be_valid }
    it { should_not be_admin }
    its(:remember_token) { should_not be_blank }
    its(:email) { should_not =~ /\p{Upper}/ }
  end

  describe "validations" do
    context "for name" do
      it { should validate_presence_of(:name) }
      it { should_not allow_value(" ").for(:name) }
      it { should ensure_length_of(:name).is_at_most(50) }
    end

    context "for email" do
      it { should validate_presence_of(:email) }
      it { should_not allow_value(" ").for(:email) }
      it { should validate_uniqueness_of(:email).case_insensitive }

      context "when email format is invalid" do
        addresses = %w[user@foo,com user_at_foo.org example.user@foo.]
        addresses.each do |invalid_address|
          it { should_not allow_value(invalid_address).for(:email) }
        end
      end

      context "when email format is valid" do
        addresses = %w[user@foo.COM A_US-ER@f.b.org frst.lst@foo.jp a+b@baz.cn]
        addresses.each do |valid_address|
          it { should allow_value(valid_address).for(:email) }
        end
      end
    end

    context "for password" do
      it { should ensure_length_of(:password).is_at_least(6) }
      it { should_not allow_value(" ").for(:password) }

      context "when password doesn't match confirmation" do
        it { should_not allow_value("mismatch").for(:password) }
      end
    end

    context "for password_confirmation" do
      it { should validate_presence_of(:password_confirmation) }
    end
  end

  #...
end

Некоторые конкретные вопросы об этих тестах:

  1. Стоит ли вообще тестировать схему базы данных? Комментарий в ответе StackOverflow, упомянутом выше , гласит: «Я проверяю только то, что связано с поведением, и я не учитываю наличие столбца или поведение индекса. Столбцы базы данных не исчезают просто так, если кто-то намеренно удаляет их, но вы можете защититься от этого с помощью проверок кода и доверия», с чем я согласен, но есть ли какая-либо веская причина, по которой будет проверена структура схемы базы данных и, таким образом, оправдывающая существование Shoulda::Matchers::ActiveRecordмодуль? Возможно, стоит протестировать только важные индексы...?
  2. Заменяют ли тесты should have_manyпо "associations"соответствующие им тесты should respond_toпо "model attributes"? Я не могу сказать, просто ли тест should have_manyищет соответствующее объявление has_manyв файле модели или на самом деле выполняет ту же функцию, что и should respond_to.
  3. Есть ли у вас какие-либо другие комментарии/предложения, чтобы сделать эти тесты более краткими/читабельными/тщательными как по содержанию, так и по структуре?

11
задан Community 23 May 2017 в 10:33
поделиться