Rails 3.1, RSpec: проверка валидации модели

Я начал свое путешествие с TDD в Rails и столкнулся с небольшой проблемой, касающейся тестов для проверки модели, которые я, похоже, не могу найти. Скажем, у меня есть модель User,

class User < ActiveRecord::Base
  validates :username, :presence => true
end

и простой тест

it "should require a username" do
  User.new(:username => "").should_not be_valid
end

Это правильно проверяет проверку присутствия, но что, если я хочу быть более конкретным? Например, тестирование full_messages на объекте ошибок.

it "should require a username" do
  user = User.create(:username => "")
  user.errors[:username].should ~= /can't be blank/
end

Моя забота об исходной попытке (с использованием should_not be_valid) заключается в том, что RSpec не выдаст описательного сообщения об ошибке. Он просто говорит: "Ожидаемый допустимый?", Чтобы вернуть false, получил истинное значение ". Однако второй тестовый пример имеет незначительный недостаток: он использует метод create вместо нового метода для доступа к объекту ошибок.

Я бы хотел, чтобы мои тесты были более конкретными в отношении того, что они тестируют, но в то же время не нужно касаться базы данных.

У кого-нибудь есть вход?

Ответ 1

Сначала я хотел бы сказать, что у вас есть злобное имя.

Во-вторых, ПОЗДРАВЛЯЕМ, что вы пытаетесь в TDD с ROR, я обещаю, как только вы пойдете, вы не оглядитесь назад.

Простейшим быстрым и грязным решением будет создание новой действующей модели перед каждым из ваших тестов следующим образом:

 before(:each) do
    @user = User.new
    @user.username = "a valid username"
 end

НО, что я предлагаю, вы создали фабрики для всех своих моделей, которые автоматически создадут для вас действительную модель, а затем вы можете запутаться с отдельными атрибутами и посмотреть, есть ли ваша проверка. Мне нравится использовать FactoryGirl для этого:

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

it "should have valid factory" do
    FactoryGirl.build(:user).should be_valid
end

it "should require a username" do
    FactoryGirl.build(:user, :username => "").should_not be_valid
end

О, да и вот хороший railscast, который объясняет все это лучше меня:

удачи:)


UPDATE: с версия 3.0 синтаксис для factory изменился. Я изменил свой пример кода, чтобы отразить это.

Ответ 2

Более простой способ проверки валидации модели (и многое другое из активной записи) заключается в использовании драгоценного камня, такого как shoulda или remarkable.

Они разрешат тест следующим образом:

describe User

  it { should validate_presence_of :name }

end

Ответ 3

Попробуйте следующее:

it "should require a username" do
  user = User.create(:username => "")
  user.valid?
  user.errors.should have_key(:username)
end

Ответ 4

в новой версии rspec, вы должны использовать expect вместо этого, иначе вы получите предупреждение:

it "should have valid factory" do
    expect(FactoryGirl.build(:user)).to be_valid
end

it "should require a username" do
    expect(FactoryGirl.build(:user, :username => "")).not_to be_valid
end

Ответ 5

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

Пример спецификации функции

before(:each) { visit_order_path }

scenario 'with invalid (empty) description' , :js => :true do

  add_empty_task                                 #this line is defined in my spec_helper

  expect(page).to have_content("can't be blank")

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

Ответ 6

Как @nathanvda сказал, я бы воспользовался Thoughtbot Assa Matches. С этим покачиванием вы можете написать свой тест следующим образом, чтобы проверить наличие, а также любое сообщение об ошибке.

RSpec.describe User do

  describe 'User validations' do
    let(:message) { "I pitty da foo who dont enter a name" }

    it 'validates presence and message' do
     is_expected.to validate_presence_of(:name).
      with_message message
    end

    # shorthand syntax:
    it { is_expected.to validate_presence_of(:name).with_message message }
  end

end