Учебное пособие Michael Hartl Rails: assert_not делает прямо противоположное тому, что должно, и я не понимаю, почему

tl;dr Допустимые имена не записываются в базу данных, потому что тест не пройден, а недопустимые имена записываются в базу данных, потому что тест проходит.

Изменить: Для разъяснения по поводу проекта и моего вопроса в целом: как описано в книге, эта модель пользователя настроена как начальная стадия, чтобы позволить пользователю веб-сайта в конечном итоге войти в веб-сайт. Столбцы базы данных будут "имя" и "электронная почта", и каждая строка будет один пользователь (при условии, что имя пользователя и адрес электронной почты были действительными). Я отредактировал свой оригинальный пост ниже для дальнейшего разъяснения, и все изменения выделены курсивом.

Кроме того, отвечайте, только если вы можете объяснить код, как в моем посте - не предлагайте добавлять дополнительный код, чтобы он работал. Учебник, над которым я работаю, утверждает, что этот код должен работать как есть, но, похоже, он оценивает противоположное, что должен. Наконец, если вы знаете другие ссылки, которые объясняют это более подробно, это полезно; однако я уже прочитал apidock, RoR API и большинство SO-ссылок, которые отображаются в поиске Google, и ни одна из них не помогла в объяснении этой проблемы.

Я работаю над учебником Майкла Хартла по Ruby on Rails. Я в главе 6 работаю с проверочными тестами и застрял в проверке на наличие имени. Похоже, что он делает прямо противоположное тому, что учебник говорит, что должен делать (то есть, проверяет недопустимую запись имени), хотя я точно следовал этому учебнику. Я также искал в Интернете и SO для получения дополнительной информации о том, как assert_not работает, но не могу найти никаких полезных деталей. Вот процесс, который я прошел.

  1. Добавьте тест, который, как мы знаем, потерпит неудачу:

    require 'test_helper'
    
    class UserTest < ActiveSupport::TestCase
    
      def setup
        @user = User.new(name: "Example User", email: "user@example.com")
      end
    
      # resolves to true and passes
      test "should be valid" do
       assert @user.valid?
      end
    
      test "name should be present" do
        @user.name = "     "
        assert_not @user.valid?
      end
    
    end
    

Все идет нормально. Я прекрасно понимаю этот код, и хотя формулировка второго теста неудобна, я интерпретирую assert_not @user.valid? работать так: @user.valid? оценивается как true, так как проверки не были установлены, поэтому тест думает, что имя со всеми пробелами является допустимым. Этому предшествует assert_not который преобразует истинное значение в ложное, и делает тест неудачным.

  1. Мы добавляем проверочный тест:

    class User < ActiveRecord::Base
      validates :name, presence: true
    end
    

Это устанавливает правило проверки для модели User, которая обеспечивает :name Объект имеет содержимое и не является nil, пустой строкой или пробелом. Пока все еще хорошо.

  1. Здесь все становится сложнее. На данный момент выражение @user.valid? в нашем тесте следует (и имеет) значение false, поскольку правило проверки не позволяет :name только с пробелами в нем. Я добавил комментарии вместе с этим кодом, чтобы помочь моему разуму увидеть, какие значения назначаются и оцениваются в этом процессе:

    test "name should be present" do
        @user.name = "   "
        # => name: "   " email: "example@example.com"
        assert_not @user.valid? 
        # => assert_not false => true
    end
    

@user.valid? оценивается как ложное, что делает весь assert_not выражение оценивается как истина, и, таким образом, тест проходит. В псевдокоде эта строка может выглядеть так: assert_not (is the user valid? no) ==> assert_not(false) ==> true , Аналогичным образом, "Мы не утверждаем, что пользователь является ложным", что оценивается как истинное.

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

  1. Наоборот, я разработал эту логику с правильным именем:

    test "name should be present" do
    # => name: "Example User" email: "example@example.com"
        assert_not @user.valid? 
        # assert_not true => false
    end
    

В этом случае, так как @user.valid? оценивается как истина (так как "Пример пользователя" является допустимым именем), assert_not @user.valid? оценивается как ложное, тест не пройден, и наше действительное имя не записывается в базу данных. В псевдокоде эта строка может выглядеть так: assert_not (is the user valid? yes) ==> assert_not(true) ==> false , Аналогичным образом, "Мы не утверждаем, что пользователь является истинным", что оценивается как ложное. Поскольку тест оценивается как ложное (даже с истинным / действительным именем пользователя), это действительное имя не записывается в базу данных.

Если бы кто-то мог объяснить, как это имеет какой-то смысл, я был бы очень признателен, и подробные ответы, проведенные со мной, были бы еще лучше

2 ответа

Тест передает недопустимое значение для:name, что позволяет записать имя, состоящее из пробелов, в базу данных...

assert_not возвращает true (так же, как прохождение теста), если следующее выражение @user.valid?, возвращает ложь. По сути, тест в разделе 3 гласит: мы не утверждаем, что пользователь действителен. Так как это ожидание / утверждение верно, оно возвращает true и тест проходит. Ничего не было сохранено в базе данных.

Вы можете добавить валидацию при добавлении строки " ", состоящей из двух пробелов

  class User < ActiveRecord::Base
   validates :name, presence: true, allow_blank: false
   validate :name_of_valid

   def name_of_valid
    self.errors.add :base, 'My string can not be empty' if self.name == "   " 
   end
end

а потом

test "name should be present" do
    @user.name = "   "
    # => name: "   " email: "example@example.com"
    assert_not @user.valid? 
    # => assert_not true 
end

У меня такая же проблема. Только сpresence: true тест не проходит, и когда я добавляю allow_blank: false это проходит.

Другие вопросы по тегам