英文:
Test Article creation within Rails/Rspec
问题
控制器在创建后重定向到article#show页面。我包括let(:article) { create :article }
,否则我会得到以下错误:
Failure/Error: expect(response).to redirect_to article_path(article)
NameError:
undefined local variable or method 'article' for #<RSpec::ExampleGroups::ArticlesController::Create::ValidParams "creates an article" (./spec/controllers/article_controller_spec.rb:27)>
无论如何,我收到以下错误,似乎创建额外的文章导致了这个问题:
Failure/Error: expect(response).to redirect_to article_path(article)
Expected response to be a redirect to <http://test.host/articles/4> but was a redirect to <http://test.host/articles/3>.
Expected "http://test.host/articles/4" to be === "http://test.host/articles/3".
articles_controller_spec.rb
require 'rails_helper'
RSpec.describe ArticlesController, type: :controller do
user = FactoryBot.create(:user)
user.confirm
describe '#create' do
before do
sign_in user
end
context 'valid params' do
let(:article) { create :article }
let(:params) {
{
article: {
title: 'New article title',
body: 'New article body',
status: 'public',
created_at: Time.now,
updated_at: Time.now,
user:
},
}
}
it 'creates an article' do
# debugger
post :create, params: params
expect(response).to redirect_to article_path(article)
end
end
end
end
articles_controller
class ArticlesController < ApplicationController
def index
@articles = Article.all
end
# Rails ACTIVERECORD method :include which loads associative records in
# advance and limits the number of SQL queries made to the DB.
# To improve performance.
def home
@user = User.includes(:articles, :comments)
end
def show
@article = Article.find(params[:id])
@comment = @article.comments.order(updated_at: :desc)
@article.update(views: @article.views + 1)
mark_notifications_as_read
end
def new
@article = Article.new
end
def create
@article = Article.new(article_params)
@article.user = current_user
if @article.save
redirect_to @article, notice: 'Article created successfully'
else
render :new, status: :unprocessable_entity
end
end
def edit
@article = Article.find(params[:id])
end
def update
@article = Article.find(params[:id])
if @article.update(article_params)
redirect_to @article, notice: 'Article updated successfully'
else
render :edit, status: :unprocessable_entity
end
end
def destroy
@article = Article.find(params[:id])
@article.destroy
redirect_to articles_path, status: :see_other, notice: 'Article deleted successfully'
end
# All private methods ensure only the class they're initialized in can access the method
private
def article_params
params.require(:article).permit(:title, :body, :status)
end
def mark_notifications_as_read
return unless current_user
notifications_to_mark_as_read = @article.notifications_as_article.where(recipient: current_user)
notifications_to_mark_as_read.update_all(read_at: Time.zone.now)
end
end
factories/article
FactoryBot.define do
factory :article do
title { Faker::Lorem.words(number: 2) }
body { Faker::Lorem.paragraphs }
created_at { Faker::Time.between(from: DateTime.now - 1, to: DateTime.now) }
updated_at { Faker::Time.between(from: DateTime.now - 1, to: DateTime.now) }
status { 'public' }
association :user, factory: :user
end
end
routes.rb
Rails.application.routes.draw do
root 'articles#home'
get 'search', to: 'search#index'
devise_for :users, controllers: {
sessions: 'users/sessions',
registrations: 'users/registrations'
}
# allows for chained url routes
# eg. /posts/1/comments/4
resources :articles do
resources :comments
end
end
rails routes
articles GET /articles(.:format) articles#index
POST /articles(.:format) articles#create
new_article GET /articles/new(.:format) articles#new
edit_article GET /articles/:id/edit(.:format) articles#edit
article GET /articles/:id(.:format) articles#show
PATCH /articles/:id(.:format) articles#update
PUT /articles/:id(.:format) articles#update
DELETE /articles/:id(.:format) articles#destroy
英文:
The controller redirects to the article#show once created. I included let(:article) { create :article }
as I would otherwise get the error
Failure/Error: expect(response).to redirect_to article_path(article)
NameError:
undefined local variable or method `article' for #<RSpec::ExampleGroups::ArticlesController::Create::ValidParams "creates an article" (./spec/controllers/article_controller_spec.rb:27)>`.
Anyway, I am receiving the below so it seems like creating the extra article causes this I think:
Failure/Error: expect(response).to redirect_to article_path(article)
Expected response to be a redirect to <http://test.host/articles/4> but was a redirect to <http://test.host/articles/3>.
Expected "http://test.host/articles/4" to be === "http://test.host/articles/3".
articles_controller_spec.rb
require 'rails_helper'
RSpec.describe ArticlesController, type: :controller do
user = FactoryBot.create(:user)
user.confirm
describe '#create' do
before do
sign_in user
end
context 'valid params' do
let(:article) { create :article }
let(:params) {
{
article: {
title: 'New article title',
body: 'New article body',
status: 'public',
created_at: Time.now,
updated_at: Time.now,
user:
},
}
}
it 'creates an article' do
# debugger
post :create, params: params
expect(response).to redirect_to article_path(article)
end
end
end
end
articles_controller
class ArticlesController < ApplicationController
def index
@articles = Article.all
end
# Rails ACTIVERECORD method :include which loads associative records in
# advance and limits the number of SQL queries made to the DB.
# To improve performance.
def home
@user = User.includes(:articles, :comments)
end
def show
@article = Article.find(params[:id])
@comment = @article.comments.order(updated_at: :desc)
@article.update(views: @article.views + 1)
mark_notifications_as_read
end
def new
@article = Article.new
end
def create
@article = Article.new(article_params)
@article.user = current_user
if @article.save
redirect_to @article, notice: 'Article created successfully'
else
render :new, status: :unprocessable_entity
end
end
def edit
@article = Article.find(params[:id])
end
def update
@article = Article.find(params[:id])
if @article.update(article_params)
redirect_to @article, notice: 'Article updated successfully'
else
render :edit, status: :unprocessable_entity
end
end
def destroy
@article = Article.find(params[:id])
@article.destroy
redirect_to articles_path, status: :see_other, notice: 'Article deleted successfully'
end
# All private methods ensure only the class they're initialized in can access the method
private
def article_params
params.require(:article).permit(:title, :body, :status)
end
def mark_notifications_as_read
return unless current_user
notifications_to_mark_as_read = @article.notifications_as_article.where(recipient: current_user)
notifications_to_mark_as_read.update_all(read_at: Time.zone.now)
end
end
factories/article
FactoryBot.define do
factory :article do
title { Faker::Lorem.words(number: 2) }
body { Faker::Lorem.paragraphs }
created_at { Faker::Time.between(from: DateTime.now - 1, to: DateTime.now) }
updated_at { Faker::Time.between(from: DateTime.now - 1, to: DateTime.now) }
status { 'public' }
association :user, factory: :user
end
end
routes.rb
Rails.application.routes.draw do
root 'articles#home'
get 'search', to: 'search#index'
devise_for :users, controllers: {
sessions: 'users/sessions',
registrations: 'users/registrations'
}
# allows for chained url routes
# eg. /posts/1/comments/4
resources :articles do
resources :comments
end
end
rails routes
articles GET /articles(.:format) articles#index
POST /articles(.:format) articles#create
new_article GET /articles/new(.:format) articles#new
edit_article GET /articles/:id/edit(.:format) articles#edit
article GET /articles/:id(.:format) articles#show
PATCH /articles/:id(.:format) articles#update
PUT /articles/:id(.:format) articles#update
DELETE /articles/:id(.:format) articles#destroy
答案1
得分: 2
关于您的第二个错误:
Failure/Error: expect(response).to redirect_to article_path(article)
期望响应是重定向到 <http://test.host/articles/4>,但实际上是重定向到 <http://test.host/articles/3>。
期望 "http://test.host/articles/4" 与 "http://test.host/articles/3" 相等。
问题出在 "expect" 块上。由于您正在执行一个POST请求来创建一个新记录,期望应该检查记录数量的增加,而不是被重定向到另一条记录,即在 "let" 块中创建的记录。
我建议更改您的测试代码,类似于以下方式:
it '创建一篇文章' do
expect { post :create, params: }.to change { Article.count }.by(1)
expect(response).to have_http_status(200)
end
请注意,我无法提供完整的代码翻译,因为您未提供请求参数的部分。如果您需要请求参数的翻译,请提供请求参数的具体内容。
英文:
Regarding your second error
Failure/Error: expect(response).to redirect_to article_path(article)
Expected response to be a redirect to <http://test.host/articles/4> but was a redirect to <http://test.host/articles/3>.
Expected "http://test.host/articles/4" to be === "http://test.host/articles/3".
The problem is the expect block. Since you are doing a POST request to create a new record, the expectation should check that there's an increase in the record count not being redirected to another record, the one created on the let block.
I would recommed changing your test to something like this:
it 'creates an article' do
expect { post :create, params: }.to change { Article.count }.by(1)
expect(response).to have_http_status(200)
end
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论