[英]Rails 6 API Rspec request test - Conflicting behavior
我需要有關當前測試方法的幫助。 我目前正在使用 Rspec 測試我的 React-Rails 應用程序,最初我在 favourite_cocktail controller 中進行了設置:
def destroy
@favouritecocktail = FavouriteCocktail.find(params[:id])
@favouritecocktail.delete
end
在使用以下代碼測試 DELETE 請求時:
describe 'DELETE /api/v1/favourite_cocktails/:id' do
let!(:users) { FactoryBot.create(:user) }
let!(:cocktails) { FactoryBot.create(:cocktail) }
let!(:favourite_cocktail) { FactoryBot.create_list(:favourite_cocktail, 10, cocktail: cocktails) }
let(:cocktail_id) { favourite_cocktail.first.id }
before do
sign_in users
end
before { delete "/api/v1/favourite_cocktails/#{cocktail_id}" }
it 'returns status code 204' do
expect(response).to have_http_status(204)
end
end
它通過了,但在我的應用程序上,負責刪除用戶最喜歡的雞尾酒的 function 不起作用。 那就是當我單擊一個按鈕以刪除用戶最喜歡的雞尾酒時,它不起作用。
但是,如果我將 favourite_cocktail controller 中的銷毀操作方法重構為:
def destroy
@favouritecocktail = current_user.favourite_cocktails.find_by(cocktail_id: params[:id])
@favouritecocktail.delete
end
function 負責刪除應用程序上用戶最喜歡的雞尾酒作品。 但是當我再次運行測試時:
describe 'DELETE /api/v1/favourite_cocktails/:id' do
let!(:users) { FactoryBot.create(:user) }
let!(:cocktails) { FactoryBot.create(:cocktail) }
let!(:favourite_cocktail) { FactoryBot.create_list(:favourite_cocktail, 10, cocktail: cocktails) }
let(:cocktail_id) { favourite_cocktail.first.id }
before do
sign_in users
end
before { delete "/api/v1/favourite_cocktails/#{cocktail_id}" }
it 'returns status code 204' do
expect(response).to have_http_status(204)
end
end
它失敗了,這是我在 RSpec 測試期間收到的錯誤消息:
Api::V1::FavouriteCocktailsController DELETE /api/v1/favourite_cocktails/:id returns status code 204
Failure/Error: @favouritecocktail.delete
NoMethodError:
undefined method `delete' for nil:NilClass
# ./app/controllers/api/v1/favourite_cocktails_controller.rb:47:in `destroy'
# ./spec/requests/favourite_cocktails_spec.rb:80:in `block (3 levels) in <main>'
# ./spec/rails_helper.rb:112:in `block (3 levels) in <top (required)>'
# ./spec/rails_helper.rb:111:in `block (2 levels) in <top (required)>'
現在我想要的首選方法是我的 remove favourite_cocktail 應該在應用程序上運行,並且 Rspec 測試應該命中 DELETE 路由,以便它通過。 我知道使用 FactoryBot 時沒有創建 favourite_cocktails 的記錄,我關心的是如何讓 FactoryBot 創建要刪除的記錄。 以下是 API 的代碼:
寶石文件
ruby '2.6.1'
# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
gem 'rails', '~> 6.0.2', '>= 6.0.2.2'
# Use postgresql as the database for Active Record
gem 'pg', '>= 0.18', '< 2.0'
# Use Puma as the app server
gem 'puma', '~> 4.1'
# Use SCSS for stylesheets
gem 'sass-rails', '>= 6'
# Transpile app-like JavaScript. Read more: https://github.com/rails/webpacker
gem 'webpacker', '~> 4.0'
# Turbolinks makes navigating your web application faster. Read more: https://github.com/turbolinks/turbolinks
gem 'turbolinks', '~> 5'
# Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
gem 'jbuilder', '~> 2.7'
# Use Redis adapter to run Action Cable in production
# gem 'redis', '~> 4.0'
# Use Active Model has_secure_password
# gem 'bcrypt', '~> 3.1.7'
# Use Active Storage variant
# gem 'image_processing', '~> 1.2'
# Reduces boot times through caching; required in config/boot.rb
gem 'bootsnap', '>= 1.4.2', require: false
gem 'devise'
gem 'react-rails'
gem "font-awesome-rails"
gem 'foreman'
group :development, :test do
# Call 'byebug' anywhere in the code to stop execution and get a debugger console
gem 'byebug', platforms: [:mri, :mingw, :x64_mingw]
# gem 'rspec-rails', '~> 3.8'
gem 'rspec-rails', git: 'https://github.com/rspec/rspec-rails', branch: "4-0-maintenance"
end
group :development do
gem 'guard-rspec', require: false
gem 'listen', '>= 3.0.5', '< 3.2'
gem 'rb-fsevent', '~> 0.10.3'
gem 'spring'
gem 'spring-watcher-listen', '~> 2.0.0'
gem 'web-console', '>= 3.3.0'
end
group :test do
gem 'database_cleaner'
gem 'factory_bot_rails'
gem 'faker'
gem 'shoulda-matchers'
end
路線.rb
Rails.application.routes.draw do
devise_for :users
get 'landing/index'
get '/index', to: 'landing#index', as: 'index'
namespace :api do
namespace :v1 do
resources :cocktails do
put :favourite, on: :member
end
resources :favourite_cocktails, only: %i[create destroy]
resources :favourites_dashboard, only: %i[index]
end
end
root 'landing#app'
match '*path', to: 'landing#app', via: :all
end
Favourite_cocktails controller
module Api
module V1
class FavouriteCocktailsController < ApplicationController
skip_before_action :verify_authenticity_token
def index
@favouritecocktail = current_user.cocktails
if user_signed_in? && @favouritecocktail
render json: {status: 'SUCCESS', message: 'Loading all Favourite Cocktails', data: @favouritecocktail}, status: :ok
else
render json: {}, status: 401
end
end
def create
fav = FavouriteCocktail.new(favourite_params) do |c|
c.user = current_user
end
if fav.save!
render json: { message: 'created' }, status: :created
else
render json: { errors: fav.errors.full_messages },
status: :unprocessable_entity
end
end
def destroy
@favouritecocktail = current_user.favourite_cocktails.find_by(cocktail_id: params[:id])
@favouritecocktail.delete
end
private
def favourite_params
params.require(:favourite_cocktail).permit(:cocktail_id)
end
end
end
end
最喜歡的雞尾酒工廠
FactoryBot.define do
factory :favourite_cocktail do
user
cocktail
end
end
用戶工廠
FactoryBot.define do
factory :user do
username { Faker::Name.name }
email { Faker::Internet.safe_email }
password { 'foobar' }
password_confirmation { 'foobar' }
end
factory :random_user, class: User do
username { Faker::Name.name }
email { Faker::Internet.safe_email }
password { Faker::Password.password }
password_confirmation { Faker::Password.password_confirmation }
end
end
雞尾酒廠
FactoryBot.define do
factory :cocktail do
name { Faker::Restaurant.name }
description { Faker::Lorem.sentence }
ingredients { Faker::Lorem.sentence }
image { Faker::Avatar.image }
end
end
協會
最喜歡的雞尾酒
class FavouriteCocktail < ApplicationRecord
belongs_to :user
belongs_to :cocktail
validates :user_id, uniqueness: { scope: :cocktail_id }
end
用戶
class User < ApplicationRecord
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable
has_many :favourite_cocktails
has_many :favourites, through: :favourite_cocktails, source: :cocktail
validates :username, presence: true, uniqueness: true, allow_blank: false, length: { minimum: 5 }
validates :email, presence: true, length: { minimum: 5 }
end
雞尾酒
class Cocktail < ApplicationRecord
has_many :favourite_cocktails
has_many :favourited, through: :favourite_cocktails, source: :user
validates :name, presence: true, allow_blank: false, length: { minimum: 5 }
validates :description, presence: true, allow_blank: false, length: { minimum: 10 }
validates :ingredients, presence: true, allow_blank: false, length: { minimum: 10 }
validates :image, presence: true
end
RSpec
最喜歡的雞尾酒要求規格
require 'rails_helper'
RSpec.describe Api::V1::FavouriteCocktailsController, type: :request do
describe 'POST Favourite Cocktails' do
let!(:users) { FactoryBot.create(:user) }
let!(:cocktails) { FactoryBot.create_list(:cocktail, 10) }
let!(:favourite_cocktail) { FactoryBot.create_list(:favourite_cocktail, 10) }
let(:cocktail_id) { cocktails.first.id }
let(:valid_params) do
{ favourite_cocktail: { cocktail_id: cocktails.first.id } }
end
before do
sign_in users
end
context 'when the request is valid' do
before { post '/api/v1/favourite_cocktails', params: valid_params }
it 'returns status code 201' do
expect(response).to have_http_status(201)
end
it 'returns a created status' do
expect(response).to have_http_status(:created)
end
end
end
describe 'GET all favourite cocktails' do
let!(:users) { FactoryBot.create(:user) }
let!(:favourite_cocktail) { FactoryBot.create_list(:favourite_cocktail, 10) }
let(:cocktail_id) { cocktails.first.id }
before do
sign_in users
get '/api/v1/favourite_cocktails'
end
it 'returns HTTP status 200' do
expect(response).to have_http_status 200
end
end
describe 'DELETE /api/v1/favourite_cocktails/:id' do
let!(:users) { FactoryBot.create(:user) }
let!(:cocktails) { FactoryBot.create(:cocktail) }
let!(:favourite_cocktail) { FactoryBot.create_list(:favourite_cocktail, 10, cocktail: cocktails) }
let(:cocktail_id) { favourite_cocktail.first.id }
before do
sign_in users
end
before { delete "/api/v1/favourite_cocktails/#{cocktail_id}" }
# thing = create(:thing)
# delete '/things', :thing => { :id => thing.id'}
it 'returns status code 204' do
expect(response).to have_http_status(204)
end
end
end
如果您希望看到其他的東西來讓這個工作,請告訴我。 謝謝你的幫助。
在我看來,您的 RSpec 測試設置沒有重新創建有效的“快樂路徑”場景,因為您嘗試刪除的雞尾酒實際上並不屬於您登錄的用戶。
這是我認為應該有助於修復測試的輕微重構:
describe 'DELETE /api/v1/favourite_cocktails/:id' do
let!(:user) { FactoryBot.create(:user) }
let!(:cocktail) { FactoryBot.create(:cocktail) }
# Adding `user: user` is the important bit here according to your factory
let!(:favourite_cocktail) { FactoryBot.create(:favourite_cocktail, user: user cocktail: cocktail) }
let(:cocktail_id) { favourite_cocktail.id }
before do
sign_in users
end
before { delete "/api/v1/favourite_cocktails/#{cocktail_id}" }
it 'returns status code 204' do
expect(response).to have_http_status(204)
end
end
這是我對刪除實現所做的更改,以便嘗試刪除您不喜歡的雞尾酒不會引發異常和 500:
def destroy
@favouritecocktail = current_user.favourite_cocktails.find_by(cocktail_id: params[:id])
@favouritecocktail.delete if @favouritecocktail
end
當然,即使刪除失敗也會返回成功,但如果這比您的應用程序更可取,您可以輕松發送 400 級響應。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.