Как проверить "Model.valid?"на модели, сгенерированной FactoryBot с использованием Rspec? - PullRequest
0 голосов
/ 21 октября 2019

Я создал модель комнаты чата, которая сначала проверяет наличие некоторых полей, поэтому в контроллере я создаю комнату чата, а затем проверяю ее действительность, используя метод .valid? для определения ответа. Теперь, когда я создал тестовую модель с использованием FactoryBot, тест не проходит после оператора if и возвращает ответ, как будто тест завершен.

Код для моего действия

def create
    new_chatroom = Chatroom.create(chatroom_params)
    if new_chatroom.valid?
      new_chatroom.members.create({ user_id: @current_user[:username] })
      render_response(new_chatroom, :created)
    else
      render_error_response(new_chatroom.errors, :bad_request)
    end
  end

Код для фабрики

FactoryBot.define do

  factory :chatroom do
    topic { Faker::Lorem.unique.question }
    slug { Faker::IndustrySegments.unique.sub_sector }
    description { Faker::Lorem.paragraph }
    owner { Faker::Name.first_name }
    public { true }
  end
end

Вот мой тест

it "creates a new chatroom" do
      post :create, params: {
        :topic => "test chatroom",
          :slug => "code-testing",
          :description => "Testing with Rspec",
      }
      expect(response).to have_http_status(:created)
    end

Вот метод render_response:

def render_response(resource, status)
    if block_given?
      yield(resource, status)
    else
      render json: resource, :status => status
    end
  end

Ошибка теста:

Failure/Error: expect(response).to have_http_status(:created)
       expected the response to have status code :created (201) but it was :ok (200)

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

Ответы [ 2 ]

0 голосов
/ 22 октября 2019

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

context 'valid' do
  let(:chatroom) { create :chatroom }

  it { expect(chatroom).to be_valid }
end

context 'fails validation' do
  let(:chatroom) { create :chatroom, topic: nil }

  it { expect(chatroom).not_to be_valid }
end

Ресурс: https://www.rubydoc.info/gems/rspec-rails/RSpec%2FRails%2FMatchers:be_valid

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

it { should validate_presence_of :topic }
it { should validate_presence_of :slug }

Ресурс: https://github.com/thoughtbot/shoulda-matchers

0 голосов
/ 21 октября 2019

но я думал, что FactoryBot берет на себя все создание модели в тестах.

Нет - FactoryBot просто предоставляет фабрики, которые создают экземпляры модели. Это широко используется в качестве замены для приспособлений для заполнения базы данных перед тестами. В отличие от приборов это не происходит автоматически.

Простое добавление FactoryBot абсолютно ничего не меняет в вашем приложении, кроме того факта, что генераторы создадут заводской файл. Это никак не влияет на поведение ваших моделей.

При тестировании создания ресурсов вам необходимо проверить, что:

  1. При наличии правильных параметров, модель должна сохранятьсяв базу данных
  2. Если заданы действительные параметры, то ответ должен быть успешным и указать на вновь созданный ресурс.
  3. Если заданы недопустимые параметры, модель не должна сохраняться в базе данных
  4. Если указаны недопустимые параметры, ответ должен быть 422. Должна быть отображена страница с ошибкой.

Вы хотите проверить это с помощью спецификации запроса, а не спецификации контроллера.

Спецификации запроса предоставляют высокоуровневую альтернативу спецификациям контроллера. Фактически, начиная с RSpec 3.5, команды Rails и RSpec не рекомендуют напрямую тестировать контроллеры в пользу функциональных тестов, таких как спецификации запросов.

require "rails_helper"

RSpec.describe "Chatroom creation", type: :request do

 let(:valid_params) do
   {
     chatroom: {
       topic: "test chatroom",
       slug: "code-testing",
       description: "Testing with Rspec"
     }
   }
  end

  let(:invalid_params) do
   {
     chatroom: {
       topic: ''
     }
   }
  end

  context "when the parameters are valid" do
    it "creates a new chatroom" do
      expect do
        post '/chatrooms', params: valid_params
      end.to change(Chatroom, :count).by(1)
    end

    it "returns success" do
      post '/chatrooms', params: valid_params
      expect(response).to have_http_status(:created)
    end
  end 

  context "when the parameters are invalid" do
    it "does not create a new chatroom" do
      expect do
        post '/chatrooms', params: invalid_params
      end.to_not change(Chatroom, :count)
    end

    it "returns bad entity" do
      post '/chatrooms', params: invalid_params
      expect(response).to have_http_status(:unprocessable_entity)
    end
  end
end

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

class ChatroomsController < ApplicationController
  # ...
  def create
    new_chatroom = Chatroom.new(chatroom_params)
    if new_chatroom.save
      new_chatroom.members.create(user_id: @current_user[:username])
      render_response(new_chatroom, :created)
    else
      render_error_response(new_chatroom.errors, :bad_request)
    end
  end
end

Никогда не используйте .valid?, чтобы проверить, была ли запись сохранена в базе данных. Это только гарантирует, что проверки на уровне модели пройдены. Не то чтобы оператор INSERT из .create фактически создал строку в базе данных. См. Проверка опасности уникальности для примера того, что может произойти.

Хотя вы можете использовать new_chatroom.persisted?, это идиома распространенных рельсов, поскольку она дает вам переменную, которой вы можете манипулировать дозапись сохраняется.

class ChatroomsController < ApplicationController
  # ...
  def create
    new_chatroom = Chatroom.new(chatroom_params)
    new_chatroom.members.new(user: current_user)
    if new_chatroom.save
      render_response(new_chatroom, :created)
    else
      render_error_response(new_chatroom.errors, :bad_request)
    end
  end
end
...