I have a "Book" model and a "BookFormat" model (for "hardcover", "paperback" and so on).
The book model belongs_to book_format, and book_format has_many books. The book model hence has a book_format_id for the association. However this rspec test fails - the result is a 422 (unprocessable entity) instead of a 3xx (redirect). Not sure what I am doing wrong.
require 'rails_helper'
RSpec.describe BooksController, type: :controller do
context 'Basic book routes' do
... (stuff omitted)
describe 'CREATE /new' do
it 'creates a book when the title and format is present' do
post :create, params: { book: { title: 'A new book', book_format_id: '0' } }
expect(response).to redirect_to(assigns(:book))
end
end
end
end
The exact error from running rspec is:
1) BooksController Basic book routes GET /show CREATE /new creates a book when the title and format is present
Failure/Error: expect(response).to redirect_to(assigns(:book))
Expected response to be a <3XX: redirect>, but was a <422: Unprocessable Entity>
Response body:
# ./spec/requests/books_spec.rb:30:in `block (5 levels) in <top (required)>'
Amended test - trying to ensure that the book_format ID really exists:
require 'rails_helper'
RSpec.describe BooksController, type: :controller do
before(:all) do
#hardcover = create(:hardcover)
end
context 'Basic book routes' do
describe 'CREATE /new' do
it 'creates a book when the title and format is present' do
post :create, params: { book: { title: 'A new book', book_format_id: #hardcover.id } }
debugger
expect(response).to redirect_to(assigns(:book))
end
And I have a factory:
FactoryBot.define do
factory :hardcover, class: BookFormat do
format { 'Hardcover' }
end
end
Related
I'm studying rails and rspec.
And I made rspec unit test (request test) on rails application.
But after searching on google, I'm wonder if my job is on right way.
Can my code be a "Unit test by function(not a method, web site's feature ex)create, show, delete..) of rails application" ?
this is my code with request test.
require 'rails_helper'
RSpec.describe 'Users', type: :request do
let!(:users) { create_list(:user, 10) }
let(:user_id) { users.first.id }
let(:user) { create(:user) }
def send_request_to_store_user(name, mailaddress)
post '/users', params: {
user: {
name: users.first.name,
mailaddress: users.first.mailaddress
}
}
end
def http_status_success_and_body_element_check(body_element)
expect(response).to have_http_status(:success)
expect(response.body).to include(body_element)
end
describe 'GET' do
context 'Get /users test' do
it 'test user list page' do
get '/users'
http_status_success_and_body_element_check('User List')
end
end
context 'Get /users/create test' do
it 'test user create page' do
get '/users/create'
http_status_success_and_body_element_check('create user')
end
end
context 'Get /users/:id/edit' do
it 'test user edit page' do
get "/users/#{user_id}"
http_status_success_and_body_element_check('edit user')
end
end
context 'Get /users/:id' do
it 'test user show page' do
get "/users/#{user_id}"
http_status_success_and_body_element_check('show user')
end
end
end
describe 'POST' do
context 'test store new user' do
it 'test create new user' do
send_request_to_store_user(user.name, user.mailaddress)
expect do
create(:user)
end.to change { User.count }.from(User.count).to(User.count + 1)
end
it 'test redirect after create' do
send_request_to_store_user(user.name, user.mailaddress)
expect(response).to have_http_status(302)
end
end
end
describe 'DELETE' do
it 'test delete user' do
expect do
delete "/users/#{user_id}"
end.to change { User.count }.from(User.count).to(User.count - 1)
expect(response).to have_http_status(302)
end
end
describe 'PUT' do
context 'user update' do
it 'test user information update' do
old_name = users.first.name
new_name = 'new_name'
expect do
put "/users/#{user_id}", params: {
user: {
name: new_name
}
}
end.to change { users.first.reload.name }.from(old_name).to(new_name)
expect(response).to have_http_status(:redirect)
end
end
end
end
this is my code with test on model
require 'rails_helper'
RSpec.describe User, type: :model do
it 'user must have name and mailaddress' do
user = create(:user)
expect(user).to be_valid
expect(user.name).not_to be_nil
expect(user.mailaddress).not_to be_nil
end
it 'mailaddress must include #' do
# user = FactoryBot.create(:user)
# If rails_helper.rb has config.include FactoryBot::Syntax::Methods,
# Can use shortcut. Don't have to FactoryBot.create
user = create(:user)
# Test pass if email match with regexp
expect(user.mailaddress).to match(/\A[\w+\-.]+#[a-z\d\-]+(\.[a-z]+)*\.[a-z]+\z/)
end
end
I don't think these tests are valuable (meaningful).
Here's my reasoning:
What are these tests telling you? That the Rails router is working? That the controller is responding with the right action? Neither of these are your responsibility to test. Rails has that covered.
If you want to know "does the index page render?" and "can I CRUD a user?" then write system tests with Capybara that simulate the whole flow. That way you are testing the real-world interaction with your whole system.
What I want to solve
I want the Rspec patch or put test to succeed.
I also tested PostsContoroller before this, and I am puzzled because I did not get the same error when testing PostsContoroller.
Error
Failures:
1) Api::V1::PostItemsController update Update Content
Failure/Error: patch :update, params: { post: post_params }
ActionController::UrlGenerationError:
No route matches {:action=>"update", :controller=>"api/v1/post_items", :post=>{:id=>1, :content=>"Update-Content", :status=>false, :post_id=>1}}
# ./spec/controllers/post_items_spec.rb:11:in `block (3 levels) in <main>'
Finished in 0.35529 seconds (files took 5.58 seconds to load)
5 examples, 1 failure
Code
FactoryBot
book.rb
FactoryBot.define do
factory :book, class: Post do
sequence(:id) { |n| n}
sequence(:title) { |n| "title#{n}" }
sequence(:author) { |n| "author#{n}" }
sequence(:image) { |n| "image#{n}"}
end
end
content.rb
FactoryBot.define do
factory :content, class: PostItem do
sequence(:id) { |n| n }
sequence(:content) { |n| "list#{n}"}
sequence(:status) { false }
end
end
Spec
post_items_spec.rb
require 'rails_helper'
RSpec.describe Api::V1::PostItemsController, type: :controller do
describe 'update' do
it 'Update Content' do
book = create(:book)
content = create(:content, post_id: book.id)
post_params = { id: content.id, content: 'Update-Content', status: false, post_id: book.id }
patch :update, params: { post: post_params }
json = JSON.parse(response.body)
expect(response.status).to eq(200)
expect(json['Update-Content']).to eq('Update-content')
end
end
end
Routes
**Rails.application.routes.draw do
namespace :api do
namespace :v1 do
resources :posts
resources :post_items
end
end
end
The use of controller specs is discouraged by both the Rails and RSpec teams and has been for a very long time now. You should be writing a request spec instead which sends real HTTP requests.
RSpec.describe 'Api V1 Post items', type: :request do
let(:book) { create(:book) }
describe "PATCH /api/v1/books" do
context "with valid parameters" do
subject do
patch api_v1_post_item_path(book),
params: { content: 'Update-Content' }
end
it { should be_successful }
it "updates the content" do
# refresh the record from the db
expect { book.reload }.to change(book, :title).to('Update-Content')
end
it "includes the updated entity in the response body" do
expect(response.parsed_body['content']).to eq 'Update-Content'
end
end
# #todo write specs with invalid parameters
# #todo write specs for authentication and authorization
end
end
Another problem is that you're generating IDs in your factory. Do not do this ever. When you're actually persisting records the database will automatically assign ids. When you use build_stubbed FactoryBot will create a mock id. Using a sequence to generate IDs invites bad practices such as hardcoding ids into a spec and will only cause you headaches.
If you really want to salvage that controller spec the routing error is caused by the fact that you're missing an the ID parameter - since you're calling it as patch :update, params: { post: post_params } the id parameter is buried in params[:post][:id]. So you want patch :update, params: { id: post.id, post: post_params } I don't recommend this though - get with the program and write future proof tests instead that won't let all the bugs slip though.
I am testing my controller with RSPEC using shoulda matchers while i came across the create method in my controller i cant test the save function if i try to do that i go the error
Expected response to be a <3XX: redirect>, but was a <200: OK>
i have attached my controller part and testing and route
In testing
RSpec.describe "routes for home", type: :routing do
describe 'post #create' do
before do
post :create , params: params
end
context 'when the params are correct' do
let(:params) { { restaurant: { restaurantname: "Buhari" ,location_id: 1} } }
it 'is expected save successfully and redirect_to gridpage' do
expect(assigns[:restaurant].save).to redirect_to(gridurl_path)
end
end
end
end
In controller
def create
# render plain: params
#restaurant=Restaurant.new(restaurant_params)
if #restaurant.save
redirect_to gridurl_path
else
render 'index'
end
end
In routes
post "/home/create", to: "home#create", as: :createurl
get '/home/grid', to: 'home#grid',as: :gridurl
Thank you in advance
First I suggest you read https://relishapp.com/rspec/rspec-rails/docs/controller-specs and also the other docs. They will give you a good starting point on how to test stuff with rspec.
When you look at a controller action, you are not interested on who's doing what (i.e assigns[:restaurant]) - you want to see if a redirect happens, if something is saved in the DB, etc. Think of it from the perspective of a user calling that endpoint. Does the user know all of the internals?
Here is how it should look like:
describe "routes for home", type: :controller do
describe 'post #create' do
context 'when the params are correct' do
let(:params) { { restaurant: { restaurantname: "Buhari" ,location_id: 1} } }
it 'is expected save successfully and redirect_to gridpage' do
post :create, params: params
expect(response).to redirect_to('/home/grid')
end
end
end
end
This is my Spec file:
require 'rails_helper'
RSpec.describe Programmes::ReportsController, :type => :controller do
let!(:programme) { create(:programme) }
context 'authenticated user' do
describe 'GET index' do
it 'responds with a 200 OK status code' do
get :index, params: { id: programme.id }
expect(response).to have_http_status(:success)
end
end
end
end
This is my Factory;
FactoryGirl.define do
factory :programme do
name { Faker::Lorem.word }
description { Faker::Lorem.sentence(3) }
end
end
This is my Controller;
class Programmes::ReportsController < ApplicationController
def index
end
def create
end
end
I can't seem to get this spec to pass. The route works fine in the browser; eg
http://localhost:3000/programmes/{:id}/reports
The error I have is:
Failures:
1) Programmes::ReportsController authenticated user GET index responds with a 200 OK status code
Failure/Error: let!(:programme) { create(:programme) }
NoMethodError:
undefined method `create' for #<RSpec::ExampleGroups::ProgrammesReportsController::AuthenticatedUser::GETIndex:0x007fac78b1b440>
# /Users/mike/.rvm/gems/ruby-2.2.3/gems/actionpack-5.0.0/lib/action_dispatch/testing/assertions/routing.rb:172:in `method_missing'
I am quite new to Ruby (and Rails). I don't think the Programme object is being created in FactoryGirl - but I don't really know how to find out if that's the case
Did you require 'factory_girl' in spec_helper?
hello i'm doing some test of my application with Rspec (this is my very first time i'm using it)
this is my test file located in spec/controllers/recipes_controller_spec.rb:
require 'spec_helper'
describe RecipesController do
render_views
describe "index" do
before do
Recipe.create!(name: 'Baked Potato w/ Cheese')
Recipe.create!(name: 'Garlic Mashed Potatoes')
Recipe.create!(name: 'Potatoes Au Gratin')
Recipe.create!(name: 'Baked Brussel Sprouts')
xhr :get, :index, format: :json, keywords: keywords
end
subject(:results) { JSON.parse(response.body) }
def extract_name
->(object) { object["name"] }
end
context "when the search finds results" do
let(:keywords) { 'baked' }
it 'should 200' do
expect(response.status).to eq(200)
end
it 'should return two results' do
expect(results.size).to eq(2)
end
it "should include 'Baked Potato w/ Cheese'" do
expect(results.map(&extract_name)).to include('Baked Potato w/ Cheese')
end
it "should include 'Baked Brussel Sprouts'" do
expect(results.map(&extract_name)).to include('Baked Brussel Sprouts')
end
end
context "when the search doesn't find results" do
let(:keywords) { 'foo' }
it 'should return no results' do
expect(results.size).to eq(0)
end
end
end
end
when i try to execute it by the command:
bundle exec rspec spec/controllers/recipes_controller_spec.rb
i fail all my tests with this error:
Failure/Error: xhr :get, :index, format: :json, keywords: keywords
NameError:
uninitialized constant RecipesController::Recipes
# ./app/controllers/recipes_controller.rb:4:in `index'
# ./spec/controllers/recipes_controller_spec.rb:12:in `block (3 levels) in <top (required)>'
i've tried to look all my code but i haven't find out the error
NameError: uninitialized constant RecipesController::Recipes
means you used Recipes instead of Recipe somewhere (line 4 in index) in controller, and since your model is called Recipe (singular), you're getting NameError exception.