Testing requests with rspec

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

Testing requests with rspec

问题

I've translated the code part as requested:

module Api
  module V1
    class PaymentsController < ApplicationController
      before_action :authenticate_user!
      before_action :set_payment, only: %i[show edit update destroy]
      
      # GET /payments or /payments.json
      def index
        if user_signed_in?
          @payments = current_user.payments.order(created_at: :desc)
          render json: @payments.to_json(include: %i[home user])
        else
          render json: {}, status: 401
        end
      end

      # GET /payments/1 or /payments/1.json
      def show
        @payment = current_user.payments.find(params[:id])
        render json: @payment.to_json(include: %i[home user])
      end

      # GET /payments/new
      def new
        @home = Home.find(params[:home_id])
        @payment = @home.payments.new
      end

      # GET /payments/1/edit
      def edit; end

      # POST /payments
      def create
        @home = Home.find(params[:home_id])
        @payment = @home.payments.create(payment_params) do |p|
          p.user = current_user # if user_signed_in?
        end
        if @payment.save
          render json: @payment.to_json(include: %i[home user])
        else
          render json: @payment.errors, status: :unprocessable_entity
        end
      end

      # PATCH/PUT /payments/1 or /payments/1.json
      def update
        @home = Home.find(params[:home_id])
        @payment = @home.payments.find(params[:id])
        if @payment.update
          render json: { notice: 'Payment was successfully updated.' },
                 status: :ok
        else
          render json: { error: 'Unable to update payment' },
                 status: :unprocessable_entity
        end
      end

      # DELETE /payments/1 or /payments/1.json
      def destroy
        @home = Home.find(params[:home_id])
        @payment = @home.payments.find(params[:id])
        @payment.destroy
        render json: { notice: 'Payment succefully removed' }
      end

      private

      # Use callbacks to share common setup or constraints between actions
      def set_payment
        @payment = Payment.find(params[:id])
      end

      # Only allow a list of trusted parameters through.
      def payment_params
        params.require(:payment).permit(:first_name, :last_name, :phone_number, :address, :money_paid, :date,
                                        :nin_number, :user_id, :home_id)
      end
    end
  end
end

If you need further assistance with this code or have specific questions, feel free to ask.

英文:
  • hey guys, am trying to build tests for my controllers using rspec , I have 3 models and controllers i.e user, payment and home
    -These mode;s are associated in search a way that, payment belongs to user and home, home has many payments, and user has many payments.

-I have managed to write tests for these associations for the models , now am stuck forthe controllers.

  • Below is what i have tried to write
require 'rails_helper'
RSpec.describe 'Payments', type: :request do
let(:valid_attributes) do
{ first_name: 'mutebi', last_name: 'godfrey', phone_number: '+256780140670', address: 'kampala', money_paid: '6000', date: '25/08/2023',
nin_number: 'KK45896587450P', user_id: '5', home_id: '9' }
end
let(:invalid_attributes) do
skip('Add a hash of attributes invalid for your model')
end
describe 'GET /index' do
it 'renders a successful response' do
Payment.create! valid_attributes
get payments_url, headers: valid_headers, as: :json
expect(response).to be_successful
end
end
describe 'GET /show' do
it 'renders a successful response' do
payment = Payment.create! valid_attributes
get payment_url(payment), as: :json
expect(response).to be_successful
end
end
end
  • and getting this error below in the console. i think the problem could be with the associations
  1) Payments GET /index renders a successful response
Failure/Error: Payment.create! valid_attributes
ActiveRecord::RecordInvalid:
Validation failed: User must exist, Home must exist
# ./spec/requests/payments_spec.rb:17:in `block (3 levels) in <main>'
2) Payments GET /show renders a successful response
Failure/Error: payment = Payment.create! valid_attributes
ActiveRecord::RecordInvalid:
Validation failed: User must exist, Home must exist
# ./spec/requests/payments_spec.rb:25:in `block (3 levels) in <main>'
  • Below is my controller which am trying to test
# frozen_string_literal: true
module Api
module V1
class PaymentsController < ApplicationController
before_action :authenticate_user!
before_action :set_payment, only: %i[show edit update destroy]
# GET /payments or /payments.json
def index
if user_signed_in?
@payments = current_user.payments.order(created_at: :desc)
render json: @payments.to_json(include: %i[home user])
else
render json: {}, status: 401
end
end
# GET /payments/1 or /payments/1.json
def show
@payment = current_user.payments.find(params[:id])
render json: @payment.to_json(include: %i[home user])
end
# GET /payments/new
def new
@home = Home.find(params[:home_id])
@payment = @home.payments.new
end
# GET /payments/1/edit
def edit; end
# POST /payments
def create
@home = Home.find(params[:home_id])
@payment = @home.payments.create(payment_params) do |p|
p.user = current_user # if user_signed_in?
end
if @payment.save
render json: @payment.to_json(include: %i[home user])
else
render json: @payment.errors, status: :unprocessable_entity
end
end
# PATCH/PUT /payments/1 or /payments/1.json
def update
@home = Home.find(params[:home_id])
@payment = @home.payments.find(params[:id])
if @payment.update
render json: { notice: 'Payment was successfully updated.' },
status: :ok
else
render json: { error: 'Unable to update payment' },
status: :unprocessable_entity
end
end
# DELETE /payments/1 or /payments/1.json
def destroy
@home = Home.find(params[:home_id])
@payment = @home.payments.find(params[:id])
@payment.destroy
render json: { notice: 'Payment succefully removed' }
end
private
# Use callbacks to share common setup or constraints between actions
def set_payment
@payment = Payment.find(params[:id])
end
# Only allow a list of trusted parameters through.
def payment_params
params.require(:payment).permit(:first_name, :last_name, :phone_number, :address, :money_paid, :date,
:nin_number, :user_id, :home_id)
end
end
end
end

答案1

得分: 0

你似乎没有真正理解工厂是什么以及如何使用它们。工厂应该生成您可以在测试中使用的唯一数据。

因此,要清理您的工厂,您可以使用 ffaker gem 生成伪随机数据,而不是使用您自己的个人数据(不应该这样使用):

FactoryBot.define do
  factory :payment do
    first_name { FFaker::Name.first_name }
    last_name { FFaker::Name.last_name }
    phone_number { FFaker::PhoneNumberNL.international_mobile_phone_number }
    address { FFaker::Address.street_address }
    money_paid { FFaker::Number.decimal }
    nin_number { FFaker::Identification.ssn }
    user # 不要硬编码 ID!
    home # 不要硬编码 ID!
  end
end

在测试中使用伪随机数据可以避免您的测试依赖于测试之外的信息。这是您使用工厂而不是固定装置的最重要原因之一。

然后,在测试设置阶段中在您的规格中使用工厂:

require 'rails_helper'

RSpec.describe 'Payments', type: :request do
  let(:payment) { FactoryBot.create(:payment) }

  describe 'GET /index' do
    let!(:payments) { FactoryBot.create_list(:payment, 5) }
    it 'renders a successful response' do
      get payments_url, headers: valid_headers, as: :json
      expect(response).to be_successful
    end
  end

  describe 'GET /show' do
    it 'renders a successful response' do
      get payment_url(payment), as: :json
      expect(response).to be_successful
    end

    it 'has the correct JSON' do
      get payment_url(payment), as: :json
      expect(response.parsed_body).to match(a_hash_including(
        payment.attributes.slice(
          "first_name", "last_name", "address" # ...
        )
      ))
    end
  end
end
英文:

You haven't really understood what factories are or how to use them. Factories should generate unique data that you can use in your tests.

So to clean up your factory you could use the ffaker gem to generate psuedo random data instead of your own personal data (which you shouldn't be using like this):

FactoryBot.define do 
  factory :payment do 
    first_name { FFaker::Name.first_name } 
    last_name { FFaker::Name.last_name } 
    phone_number { FFaker::PhoneNumberNL.international_mobile_phone_number } 
    address { FFaker::Address.street_address } 
    money_paid { FFaker::Number.decimal } 
    nin_number { FFaker::Identification.ssn } 
    user # DO NOT HARDCODE ID's!
    home # DO NOT HARDCODE ID's!
  end
end

Using psuedo random data in your tests avoids your tests becoming reliant on information outside the test. This one of the biggest reasons why you would use factories instead of fixtures.

You then use the factory in your specs in the test setup phase:

require 'rails_helper'

RSpec.describe 'Payments', type: :request do
  let(:payment) { FactoryBot.create(:payment) }

  describe 'GET /index' do
    let!(:payments) { FactoryBot.create_list(:payment, 5) }
    it 'renders a successful response' do
      get payments_url, headers: valid_headers, as: :json
      expect(response).to be_successful
    end
  end

  describe 'GET /show' do
    it 'renders a successful response' do
      get payment_url(payment), as: :json
      expect(response).to be_successful
    end

    it 'has the correct JSON' do
      get payment_url(payment), as: :json
      expect(response.parsed_body).to match(a_hash_including(
        payment.attributes.slice(
          "first_name", "last_name", "address" # ...
        )
      ))
    end
  end
end

huangapple
  • 本文由 发表于 2023年4月13日 16:23:39
  • 转载请务必保留本文链接:https://go.coder-hub.com/76003241.html
匿名

发表评论

匿名网友

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

确定