Неизвестные ошибки атрибутов, выдаваемые Rspec при попытке протестировать вложенный атрибут

#ruby-on-rails #ruby #rspec

#ruby-on-rails #ruby #rspec

Вопрос:

Я создал приложение на основе курса Hartl — вплоть до раздела 9.2 (аутентификация) включительно. Оттуда я попытался расширить функциональность, добавив организационную модель. Сначала я попытался использовать функцию командной строки scaffold generate, решил, что она слишком много построила, откатила ее, а затем построила вручную. Я считаю, что в этом беспорядке я, возможно, испортил свои миграции (см. Ниже). Я использую Rails версии 4.1.1, Rspec 3.0.0 и Capybara 2.2.0 — все базы данных являются postgres.

В организации может быть много пользователей, тогда как каждый пользователь принадлежит только одной организации.

Я должен отметить, что, используя среду разработки на моем собственном компьютере, у меня есть поле для организации, работающее корректно во время регистрации пользователя — оно создает нового пользователя, я могу правильно просматривать страницы организации и пользователя, и в производительности приложения нет ошибок. Мои проблемы начинаются, когда я тестирую с использованием тестов Rspec — выдает ряд ошибок, подобных этому:

 ActiveModel::MissingAttributeError:
   can't write unknown attribute 'organization_id'
 

Мои модели:

 class Organization < ActiveRecord::Base
  validates :organization_name, presence: true, length: { maximum: 50 }
  has_many :users
end

class User < ActiveRecord::Base
  belongs_to :organization
  accepts_nested_attributes_for :organization
  before_save { self.email = email.downcase }
  before_create :create_remember_token, :create_organization
  validates :name, presence: true, length: {maximum: 50}
  VALID_EMAIL_REGEX = /A[w -.] @[a-zd-] (?:.[a-zd-] )*.[a-z] z/i
  validates :email, presence: true, format: {with: VALID_EMAIL_REGEX}, uniqueness: {case_sensitive: false}
  has_secure_password
  validates :password, length: {minimum: 6}

  def create_organization
    self.organization = Organization.create(:organization_name => Organization.organization_name) unless self.organization.present?
  end
 

Мои фабрики:

 FactoryGirl.define do
  factory :organization do
    organization_name "Orgname"
  end
  factory :user do
    association :organization
    name "A Name"
    email "name@example.com"
    password "foobar"
    password_confirmation "foobar"
  end
end
 

Пример набора тестов Rspec:

 describe "edit" do
  let(:organization) { FactoryGirl.create(:organization) }
  let(:login) { FactoryGirl.create(:login,
                                   :organization => organization,
                                   :user => FactoryGirl.create(:user)) }
  before do
    sign_in login
    visit edit_user_path (user)
  end
 

Мой файл schema.rb:

 ActiveRecord::Schema.define(version: 20140619041326) do

  create_table "organizations", force: true do |t|
    t.datetime "created_at"
    t.datetime "updated_at"
    t.string "organization_name"
  end

  create_table "users", force: true do |t|
    t.string "name"
    t.string "email"
    t.datetime "created_at"
    t.datetime "updated_at"
    t.string "password_digest"
    t.integer "organization_id"
    t.string "remember_token"
  end

  add_index "users", ["email"], name: "index_users_on_email", unique: true, using: :btree
  add_index "users", ["organization_id"], name: "index_users_on_organization_id", using: :btree
  add_index "users", ["remember_token"], name: "index_users_on_remember_token", using: :btree

end
 

Здесь, возможно, стоит отметить, что, когда я впервые начал запускать тесты, я использовал версию Rspec, рекомендованную в книге Хартла, что означает, что мне пришлось принудительно вносить изменения из базы данных разработки в тестовую базу данных с помощью функции командной строки bin / rake dg:migrate RAILS_ENV=test . Каждый раз, когда я запускал это, мой файл schema.rb (как указано выше) терял строку

t.integer "organization_id"

и мне пришлось бы разгребать базу данных, чтобы она снова заработала.

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

Любая помощь будет высоко оценена.

Комментарии:

1. Я обнаружил проблему — по-видимому, Rails 4 не обновляет схему автоматически с помощью ActiveRecord::Migration.maintain_test_schema! spec_helper — вместо этого вам все равно нужно использовать «rake db: test: prepare». Однако, хотя это избавило меня от некоторых ошибок, я по-прежнему получаю 3 основные ошибки: 1.) Ему не нравится мой заводской «логин» (я копировал другой код, который, казалось, предполагал, что это сработает) 2.) На пользовательских страницах rspec тесты не могут найти поле «Имя организации» 3.) В нем говорится, что метод «organization_name» не определен для функции «create_organization», указанной выше, какие-либо идеи?

Ответ №1:

Я думаю, что нет фабрики для :login … и я думаю, что метод sign_in хочет передать пользовательский объект.

Поэтому измените свое let(:login) утверждение на…

 let(:login) { FactoryGirl.create(:user, :organization => organization) }
 

Комментарии:

1. Спасибо. На самом деле я решил это, добавив признак в фабрику пользователей и передав как нового пользователя, так и признак. Будет ли ваш метод лучше или я должен просто оставить свой код как есть?

Ответ №2:

Ваш before_create метод create_organization в следующей строке…

   self.organization = Organization.create(:organization_name => Organization.organization_name) unless self.organization.present?
 

При этом вы ссылаетесь на метод класса (не объекта) Organization.organization_name , а такого метода класса не существует.

Вы могли бы создать его в Organization модели … это может быть какое-то значение по умолчанию…

 def self.organization_name
  "Default Organization Name"
end
 

Комментарии:

1. Спасибо. Мое намерение с этим кодом create_organization состояло в том, чтобы гарантировать, что любой, кто подписывается на учетную запись, в которой существует существующая организация, будет принадлежать этой организации (а не дублировать ее). Будет ли установка значения по умолчанию иметь какие-либо негативные последствия? Или есть лучший способ достичь моей цели без этого кода create_organization?

2. Как бы то ни было, если организация уже была назначена пользователю перед вызовом create , она будет вести себя так, как ожидалось. Вы хотите сказать, что если организация не была назначена, но в таблице организаций уже есть хотя бы одна организация, вы хотите использовать любую первую запись в таблице организаций?

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

4. Хорошо, код Organization.organization_name неправильный, поскольку такого метода класса нет. Похоже, что в рамках процесса регистрации пользователь может ввести название организации? И вы хотите использовать это для создания записи организации?

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