I used this tutorial to generate an API for my Rails 5 app. This is between Comments and Post tables. Post has a 1:m relationship with Comment.
But rspec fails every test. I have checked my files and hierarchy over and over again, still not seeing where the problem is.
Here is the output of rails routes
:
Prefix Verb URI Pattern Controller#Action
post_comments GET /posts/:post_id/comments(.:format) comments#index
POST /posts/:post_id/comments(.:format) comments#create
post_comment GET /posts/:post_id/comments/:id(.:format) comments#show
PATCH /posts/:post_id/comments/:id(.:format) comments#update
PUT /posts/:post_id/comments/:id(.:format) comments#update
DELETE /posts/:post_id/comments/:id(.:format) comments#destroy
post_like POST /posts/:post_id/like(.:format) posts#like
post_dislike POST /posts/:post_id/dislike(.:format) posts#dislike
posts GET /posts(.:format) posts#index
POST /posts(.:format) posts#create
post GET /posts/:id(.:format) posts#show
PATCH /posts/:id(.:format) posts#update
PUT /posts/:id(.:format) posts#update
DELETE /posts/:id(.:format) posts#destroy
my config/routes.rb
:
Rails.application.routes.draw do
resources :posts do
resources :comments
post :like
post :dislike
end
end
application_controller.rb
: (Even with ActionController::API, it does not work)
class ApplicationController < ActionController::Base
include Response
include ExceptionHandler
protect_from_forgery with: :exception
end
My comments_controller.rb
class CommentsController < ApplicationController
before_action :set_post
before_action :set_post_comment, only: [:show, :update, :destroy]
# GET /comments
# GET /comments.json
def index
#@comments = Comment.all
json_response(@post.comments)
end
# GET /comments/1
# GET /comments/1.json
def show
json_response(@comment)
end
# POST /comments
# POST /comments.json
def create
@comment = Comment.new(comment_params)
@post.comments.create!(comment_params)
json_response(@post, :created)
if @comment.save
render :show, status: :created, location: @comment
else
render json: @comment.errors, status: :unprocessable_entity
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_comment
@comment = Comment.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def comment_params
params.require(:comment).permit(:commenter, :comment, :description, :post)
end
def set_post
@post = Post.find(params[:post_id])
end
def set_post_comment
@comment = @post.comments.find_by!(id: params[:id]) if @post
end
end
Finally, the posts_controller.rb
:
class PostsController < ApplicationController
before_action :set_post, only: [:show, :update, :destroy]
# GET /posts
# GET /posts.json
def index
@posts = Post.all
json_response(@posts)
end
# GET /posts/1
# GET /posts/1.json
def show
json_response(@post)
end
# POST /posts
# POST /posts.json
def create
@post = Post.new(post_params)
json_response(@post, :created)
if @post.save
render :show, status: :created, location: @post
else
render json: @post.errors, status: :unprocessable_entity
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_post
@post = Post.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def post_params
params.require(:post).permit(:poster, :vote, :description, :comment, :user_id, :image_base)
end
end
UPDATE: showing spec files
my spec/requests/comments_spec.rb
:
require 'rails_helper'
RSpec.describe "Comments", type: :request do
# Initialize the test data
let!(:post) { create(:post) }
let!(:comments) { create_list(:comment, 20, post_id: post.id) }
let(:post_id) { post.id }
let(:id) { comments.first.id }
# Test suite for GET /posts/:post_id/comments
describe 'GET /posts/:post_id/comments' do
before { get "/posts/#{post_id}/comments" }
context 'when post exists' do
it 'returns status code 200' do
expect(response).to have_http_status(200)
end
it 'returns all post comments' do
expect(json.size).to eq(20)
end
end
context 'when post does not exist' do
let(:post_id) { 0 }
it 'returns status code 404' do
expect(response).to have_http_status(404)
end
it 'returns a not found message' do
expect(response.body).to match(/Couldn't find Post/)
end
end
end
# Test suite for GET /posts/:post_id/comments/:id
describe 'GET /posts/:post_id/comments/:id' do
before { get "/posts/#{post_id}/comments/#{id}" }
context 'when post item exists' do
it 'returns status code 200' do
expect(response).to have_http_status(200)
end
it 'returns the item' do
expect(json['id']).to eq(id)
end
end
context 'when post item does not exist' do
let(:id) { 0 }
it 'returns status code 404' do
expect(response).to have_http_status(404)
end
it 'returns a not found message' do
expect(response.body).to match(/Couldn't find Comment/)
end
end
end
# Test suite for PUT /posts/:post_id/comments
describe 'POST /posts/:post_id/comments' do
let(:valid_attributes) { { comment: 'A Comment', commenter: 'Luke Shaw' } }
context 'when request attributes are valid' do
before { post "/posts/#{post_id}/comments", params: valid_attributes }
it 'returns status code 201' do
expect(response).to have_http_status(201)
end
end
context 'when an invalid request' do
before { post "/posts/#{post_id}/comments", params: {} }
it 'returns status code 422' do
expect(response).to have_http_status(422)
end
it 'returns a failure message' do
expect(response.body).to match(/Validation failed: Commenter or Comment can't be blank/)
end
end
end
end
The spec/requests/posts_spec.rb
:
require 'rails_helper'
RSpec.describe "Posts", type: :request do
# initialize test data
let!(:posts) { create_list(:post, 10) }
let(:post_id) { posts.first.id }
describe "GET /posts" do
# make HTTP get request before each example
before { get '/posts' }
it 'returns posts' do
# Note `json` is a custom helper to parse JSON responses
expect(json).not_to be_empty
expect(json.size).to eq(10)
end
it 'returns status code 200' do
expect(response).to have_http_status(200)
end
end
# Test suite for GET /posts/:id
describe 'GET /posts/:id' do
before { get "/posts/#{post_id}" }
context 'when the record exists' do
it 'returns the post' do
expect(json).not_to be_empty
expect(json['id']).to eq(post_id)
end
it 'returns status code 200' do
expect(response).to have_http_status(200)
end
end
context 'when the record does not exist' do
let(:post_id) { 100 }
it 'returns status code 404' do
expect(response).to have_http_status(404)
end
it 'returns a not found message' do
expect(response.body).to match(/Couldn't find Post/)
end
end
end
The spec/models/post_spec.rb
:
require 'rails_helper'
RSpec.describe Post, type: :model do
# Association test
# ensure Post model has a 1:m relationship with the Comment model
it { should have_many(:comments).dependent(:destroy) }
# Validation tests
# ensure columns are present before saving
it { should validate_presence_of(:poster) }
it { should validate_presence_of(:description) }
end
ANd spec/models/comment_spec.rb
:
require 'rails_helper'
RSpec.describe Comment, type: :model do
# Association test
# ensure a comment record belongs to a single post record
it { should belong_to(:post) }
# Validation test
# ensure column name is present before saving
it { should validate_presence_of(:comment) }
it { should validate_presence_of(:commenter) }
end
UPDATE 2: Showing spec/factories/*.rb files
Showing spec/factories/posts.rb
:
FactoryBot.define do
factory :post do
image { Rack::Test::UploadedFile.new(Rails.root.join('public', 'system', 'posts', 'images', '000', '000', '001', 'original', 'img1.jpeg'), 'image/jpeg') }
poster { Faker::Lorem.sentence }
vote { Faker::Number.number(10).to_i}
description { Faker::Lorem.paragraph }
end
end
Showing spec/factories/comments.rb
:
FactoryBot.define do
factory :comment do
commenter { Faker::Lorem.sentence }
description { Faker::Lorem.paragraph }
post_id nil
end
end
My `routes show clear inheritance and I am enforcing that in my controllers. Still, I get errors like:
Failure/Error: expect(:delete => "/comments/1").to route_to("comments#destroy", :id => "1") No route matches "/comments/1" AND RuntimeError: /home/user/projects/app/public/system/posts/images/000/000/001/original/img1.jpeg file does not exist
/comments/1
where you have actually nested your routes under posts eg/posts/1/comments/1/
instead. – Taryn Eastroutes.rb
config file – i_use_the_internetspec/routing/comments_routing_spec.rb
file. I have it like this now and still all tests fail. The thing now is my expected and return values do not match in the tests because of the extrapost_id
param. Here is my output: pastebin.com/RnFHRQSt – i_use_the_internet