Rails 3.1, RSpec: проверка моделей тестирования


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

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 на объекте errors..

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, получил true."Однако второй тестовый пример имеет небольшой недостаток: он использует метод create вместо нового метода для получения объекта errors.

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

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

7 67

7 ответов:

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

во-вторых, поздравляю вас с вашим стремлением в 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 это объясняет все лучше, чем я:

удачи :)


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

более простой способ проверить валидацию модели (и многое другое из active-record) - использовать драгоценный камень, такой как должен был или замечательный.

они позволят тесту следующим образом:

describe User

  it { should validate_presence_of :name }

end

попробуйте это:

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

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

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

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

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

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")

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

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

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

немного поздно на вечеринку здесь, но если вы не хотите добавлять shoulda matchers, это должно работать с rspec-rails и factorybot:

# ./spec/factories/user.rb
FactoryBot.define do
  factory :user do
    sequence(:username) { |n| "user_#{n}" }
  end
end

# ./spec/models/user_spec.rb
describe User, type: :model do
  context 'without a username' do
    let(:user) { create :user, username: nil }

    it "should NOT be valid with a username error" do
      expect(user).not_to be_valid
      expect(user.errors).to have_key(:username)
    end
  end
end