测试在Rails/Rspec中创建文章

huangapple go评论58阅读模式
英文:

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&#39; for #&lt;RSpec::ExampleGroups::ArticlesController::Create::ValidParams &quot;creates an article&quot; (./spec/controllers/article_controller_spec.rb:27)&gt;`.

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 &lt;http://test.host/articles/4&gt; but was a redirect to &lt;http://test.host/articles/3&gt;.
Expected &quot;http://test.host/articles/4&quot; to be === &quot;http://test.host/articles/3&quot;.

articles_controller_spec.rb

require &#39;rails_helper&#39;
RSpec.describe ArticlesController, type: :controller do
user = FactoryBot.create(:user)
user.confirm
describe &#39;#create&#39; do
before do
sign_in user
end
context &#39;valid params&#39; do
let(:article) { create :article }
let(:params) {
{
article: {
title: &#39;New article title&#39;,
body: &#39;New article body&#39;,
status: &#39;public&#39;,
created_at: Time.now,
updated_at: Time.now,
user:
},
}
}
it &#39;creates an article&#39; do
# debugger
post :create, params: params
expect(response).to redirect_to article_path(article)
end
end
end
end

articles_controller

class ArticlesController &lt; 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: &#39;Article created successfully&#39;
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: &#39;Article updated successfully&#39;
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: &#39;Article deleted successfully&#39;
end
# All private methods ensure only the class they&#39;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 { &#39;public&#39; }
association :user, factory: :user
end
end

routes.rb

Rails.application.routes.draw do
root &#39;articles#home&#39;
get &#39;search&#39;, to: &#39;search#index&#39;
devise_for :users, controllers: {
sessions: &#39;users/sessions&#39;,
registrations: &#39;users/registrations&#39;
}
# 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 &lt;http://test.host/articles/4&gt; but was a redirect to &lt;http://test.host/articles/3&gt;.
Expected &quot;http://test.host/articles/4&quot; to be === &quot;http://test.host/articles/3&quot;.

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 &#39;creates an article&#39; do
expect { post :create, params: }.to change { Article.count }.by(1)
expect(response).to have_http_status(200)
end

huangapple
  • 本文由 发表于 2023年6月15日 21:43:18
  • 转载请务必保留本文链接:https://go.coder-hub.com/76483122.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定