Почему проверка «уникальность: истина» не работает в моем тесте (Rails)?

#ruby-on-rails #ruby #validation #unique #minitest

#ruby-on-rails #ruby #проверка #уникальный #minitest

Вопрос:

В моем приложении Rails я пытаюсь сохранить MAC-адреса для устройств, принадлежащих разным пользователям. Каждый MAC-адрес должен быть уникальным, поэтому я включил проверку уникальности. Сама проверка, похоже, работает, поскольку дублирующиеся записи были отклонены, когда я попытался использовать консоль Rails ( ActiveRecord::RecordNotUnique ). Однако мой тест для проверки того, что могут быть сохранены только уникальные записи, не выполняется.

Итак, мои вопросы:

  1. Почему мой тест проваливается и как я могу это исправить?
  2. Я читал в другом месте, что проверка уникальности сама по себе не является надежным способом гарантировать уникальность. Должен ли я использовать другие методы, такие before_save как обратные вызовы?

Это сообщение об ошибке, которое я получаю для теста:

Expected #<MacAddress id: nil, user_id: nil, address: "MACADD123", created_at: nil, updated_at: nil> to be nil or false

Настройка в моих файлах модели:

 # app/models/mac_address.rb

class MacAddress < ApplicationRecord
  validates :address, uniqueness: true
  belongs_to :user
end
  
 # app/models/user.rb

class User < ApplicationRecord
  has_many :mac_addresses, dependent: :destroy
end
  

Тест для проверки уникальности:

 class MacAddressTest < ActiveSupport::TestCase
test 'mac address must be unique' do
    new_mac = 'MACADD123'
    assert MacAddress.create(user: User.first, address: new_mac)
    assert MacAddress.all.pluck(:address).include?(new_mac)

    # The 'assert_not' below is failing.
    assert_not MacAddress.create(user: User.second, address: new_mac)
  end
end
  

Заранее спасибо за любую помощь.

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

1. Что касается 2) вы должны добавить ограничение уникальности в базу данных в дополнение к вашей проверке.

2. Чтобы запустить проверку , вы могли бы просто assert MacAddress.new(...).invalid? . Если вы хотите убедиться, что не было создано никаких дополнительных записей, возможно, было бы лучше проверить, что MacAddress.count остается неизменным.

3. @Stefan Я добавил ограничение уникальности в базу данных, когда создавал миграцию. Также обратите внимание на другие ваши пункты. Спасибо!

Ответ №1:

Согласно документации по create :

Обратите внимание, что для этой записи нет id . Это не было сохранено. Проверьте наличие ошибок с помощью .errors.full_messages , чтобы увидеть сбой проверки уникальности.

Результирующий объект возвращается независимо от того, был ли объект успешно сохранен в базе данных или нет.

Вы должны утверждать, что он сохранен, например:

 mac_address = MacAddress.create(...)
assert !mac_address.new_record?
  

Где это говорит вам, было ли оно сохранено или нет. В качестве альтернативы вы можете использовать, create! который вызовет ActiveRecord::RecordInvalid сбой.

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

1. Спасибо! Как вы указали, ошибка заключалась в том, что create метод вернул объект независимо от того, был ли он фактически сохранен в БД. Решается путем утверждения, сохранен он или нет.

2. В качестве примечания persisted? метод сообщает вам, была ли сохранена модель. На практике это противоположно new_record? , но может сделать ваши тесты более удобочитаемыми.

Ответ №2:

Для дальнейшего использования и для всех, кто просматривает этот вопрос — я переписал свой тест с помощью save вместо create , как показано ниже:

 test 'mac address must be unique' do
    test_address = 'MACADD123'
    original_mac = MacAddress.new(user: User.first, address: test_address)
    duplicate_mac = MacAddress.new(user: User.second, address: test_address)

    assert original_mac.save
    assert MacAddress.pluck(:address).include?(test_address)
    assert_not duplicate_mac.save
    duplicate_mac.errors.messages[:address].include?('has already been taken')
end
  

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

1. Вы также можете утверждать, что сообщение о дублировании было добавлено как ошибка, а не то, что оно не было сохранено по каким-либо причинам, которые могут быть несвязанными.