I'm new to rails api rspec and somehow I cannot make the test work. Can someone provide some inputs in the tests? The models and controllers are more like a pseudocode. I appreciate it. Thank you.
# routes.rb
Rails.application.routes.draw do
resources :users, :only [:create]
end
# app/model/user.rb
class User < ApplicationRecord
validates_uniqueness_of :name
end
# app/controllers/users_controller.rb
def create
#user = User.new(user_params)
if #user.save
head 200
else
render json: { error: 'Failed', status: 400}, status: 400
end
end
def user_params
params.require(:user).permit(:name)
end
# RSpec Test
require 'rails_helper'
RSpec.describe UsersController do
describe '#create' do
context 'the parameter "user[name]"" is blank' do
it 'creates new user' do
#Test Here
end
it 'renders empty response' do
#Test Here
end
it 'renders response with status 200' do
#Test Here
end
end
end
end
spec/models/user_spec.rb make sure you install gem shoulda-matchers and factory_bot_rails
require 'rails_helper'
RSpec.describe User, type: :model do
let(:user) { create(:user) }
describe "validation" do
it { should validate_uniqueness_of(:name) }
end
end
spec/controllers/users_controller_spec.rb
require 'rails_helper'
RSpec.describe UserssController, type: :controller do
describe "POST /users" do
it "when create user successfully return status 200" do
post :create, params: { name: "name" }
expect(response.status).to eq 200
end
it "when create user errors return status 400" do
post :create, params: { name: "name duplicate" }
expect(response.status).to eq 400
end
end
end
I hope it helps for you
Related
I am trying to write spec code for my controller it gets failed. And i am not sure where it gets failed.
Controller Code
def index
#users = User.all
end
def update
authorize! :update, #user
respond_to do |format|
if #user.update(user_params)
format.html { redirect_to user_index_path }
else
format.html { render :index }
end
end
end
private
def set_user
#user = User.find(params[:id])
end
def user_params
params.permit(:active)
end
Spec Code for the above controller
RSpec.describe UserController, type: :controller do
describe 'GET #index' do
let(:user) {User.create!(name: "hari")}
context 'with user details'do
it 'loads correct user details' do
get :index
expect(response).to permit(:user)
end
end
context 'without user details' do
it 'doesnot loads correct user details' do
get :index
expect(response).not_to permit(:user)
end
end
end
describe 'Patch #update' do
context 'when valid params' do
let(:attr) do
{active: 'true'}
end
before(:each) do
#user = subject.current_user
put :update, params: { user: attr }
#user.reload
end
it 'redirects to user_index_path ' do
expect(response).redirect_to(user_index_path)
end
it 'sets active state' do
expect(#user.active?('true')).to be true
end
end
context 'when invalid param' do
let(:attr) do
{active: 'nil'}
end
before(:each) do
#user = subject.current_user
put :update, params: { user: attr }
#user.reload
end
it 'render index' do
expect(respone.status).to eq(200)
end
it 'doesnot change active state' do
expect(#user.active?(nil)).to be true
end
end
end
end
I am just a beginner and tried the spec code for my controller by checking https://relishapp.com/rspec/rspec-rails/docs/gettingstarted. Can you help me where my spec goes wrong or could anyone give me a few test examples for these methods or could redirect me to an rspec guide? the index method is getting failed
and my
terminal log is
1) UserController GET #index with user details loads correct user details
Failure/Error: expect(response).to permit(:user)
NoMethodError:
undefined method `permit' for #<RSpec::ExampleGroups::UserController::GETIndex::WithUserDetails:0x00005614152406b0>
Did you mean? print
# ./spec/controllers/user_controller_spec.rb:10:in `block (4 levels) in <top (required)>'
I'm trying to create a User in Rspec:
require "rails_helper"
RSpec.describe UsersController, type: :controller do
describe "POST #index" do
it 'should create a user' do
post :create, {name: "name1", age: 25}.to_json, format: :json
# .............
But instead I get validation errors each time: name can't be blank, age can't be blank
The UserController accepts json:
def create
#user = User.new(params1)
if #user.save
render json: #user
else
render json: #user.errors, status: :unprocessable_entity
end
end
def params1
params.permit(:id, :name, :age)
end
The parameters sent to create shouldn't be a json String but a Hash.
From the rspec documentation :
require "rails_helper"
RSpec.describe WidgetsController, :type => :controller do
describe "responds to" do
it "responds to custom formats when provided in the params" do
post :create, { :widget => { :name => "Any Name" }, :format => :json }
expect(response.content_type).to eq "application/json"
end
end
end
So your test should be :
require "rails_helper"
RSpec.describe UsersController, type: :controller do
describe "POST #create" do
it 'should create a user' do
post :create, { user: {name: "name1", age: 25}, :format => :json }
...
I'm using Devise with Rspec. So I've two model called User and Article which is article belongs_to the user
When run rspec, i got an error which is say:
1) ArticlesController POST #create with valid attributes saves the article
Failure/Error: post :create, { article: valid_attributes }
#<Double "user"> received unexpected message :articles with (no args)
# ./app/controllers/articles_controller.rb:17:in `create'
# ./spec/controllers/articles_controller_spec.rb:43:in `block (4 levels) in <top (required)>'
Below is my articles_controller.rb
def new
#article ||= Article.new
render
end
def create
#article = #user.articles.new(article_params)
if #article.save
redirect_to articles_path, notice: "Well done brah! Your article has been publish"
else
render 'new'
end
end
spec/controllers/articles_controller_spec.rb
RSpec.describe ArticlesController, type: :controller do
let(:article) { FactoryGirl.create(:article) }
let (:valid_attributes) { FactoryGirl.attributes_for(:article) }
describe "POST #create" do
context "with valid attributes" do
it "saves the article" do
sign_in
post :create, { article: valid_attributes }
expect(Article.count).to eq(1)
end
end
end
spec/factories/articles.rb
FactoryGirl.define do
factory :article do
title "Article Title"
content "Article Content"
default_image "default_image"
user
category
end
end
Where is my mistake? I stuck here
UPDATED
spec/rails_helper.rb
RSpec.configure do |config|
config.include Devise::TestHelpers, :type => :controller
config.include ControllerHelpers, :type => :controller
end
spec/support/controller_helpers.rb
module ControllerHelpers
def sign_in(user = double('user'))
if user.nil?
allow(request.env['warden']).to receive(:authenticate!).and_throw(:warden, {:scope => :user})
allow(controller).to receive(:current_user).and_return(nil)
else
allow(request.env['warden']).to receive(:authenticate!).and_return(user)
allow(controller).to receive(:current_user).and_return(user)
end
end
end
Both of the updated file above i got it from DEvise wiki - https://github.com/plataformatec/devise/wiki/How-To:-Stub-authentication-in-controller-specs
You need to use an actual database record for your user*.
RSpec.describe ArticlesController, type: :controller do
let(:article) { FactoryGirl.create(:article) }
let(:valid_attributes) { FactoryGirl.attributes_for(:article) }
let(:user) { FactoryGirl.create(:user) }
let(:valid_session) { sign_in(user) }
describe "POST #create" do
before { valid_session }
context "with valid attributes" do
it "saves the article" do
# less prone to false positives
expect do
post :create, { article: valid_attributes }
end.to change(Article, :count).by(1)
end
end
end
end
We use expect {}.to change since you can get a false positive if the database is not cleaned out properly.
Devise::TestHelpers already has a sign_in function. So get rid of your ControllerHelpers module so that your project is not linked to the Devise or Warden internals.
I have a before_action filter and want to test that the index action is only executed if the user is logged in. Simply put, i don't know how to do this. I'm using my own simple authentication and i know i could use CanCan or similar but for my own learning i'm doing it the hard way!
ApplicationController.rb
helper_method :logged_in
helper_method :current_user
def current_user
#current_user ||= User.find_by_id(session[:current_user]) if session[:current_user]
end
def logged_in
unless current_user
redirect_to root_path
end
end
ActivitiesController.rb
before_action :logged_in
def index
#activities = Activity.all.where(user_id: #current_user)
end
Activities_Controller_spec.rb
require 'rails_helper'
RSpec.describe ActivitiesController, :type => :controller do
describe "GET index" do
before(:each) do
#activity = FactoryGirl.create(:activity)
session[:current_user] = #activity.user_id
#current_user = User.find_by_id(session[:current_user]) if session[:current_user]
end
it "shows all activities for signed in user" do
get :index, {user_id: #activity.user_id}
expect(response).to redirect_to user_activities_path
end
end
end
activities.rb(Factory)
FactoryGirl.define do
factory :activity do
association :user
title { Faker::App.name }
activity_begin { Faker::Date.forward(10) }
activity_end { Faker::Date.forward(24) }
end
end
I'm getting the following error:
Failure/Error: expect(response).to redirect_to user_activities_path
Expected response to be a redirect to <http://test.host/users/1/activities> but was a redirect to <http://test.host/>.
Expected "http://test.host/users/1/activities" to be === "http://test.host/".
After long discussion I think tests should be smth like this (it is not tested :) )
require 'rails_helper'
RSpec.describe ActivitiesController, :type => :controller do
describe "GET index" do
before(:each) do
#activity = FactoryGirl.create(:activity)
end
context 'when user is logged' do
before(:each) do
session[:current_user] = #activity.user_id
end
it "shows all activities for signed in user" do
get :index, {user_id: #activity.user_id}
expect(response).to be_success
end
end
context 'when user is anonymous' do
it "redirects user to root path" do
get :index, {user_id: #activity.user_id}
expect(response).to redirect_to root_path
end
end
end
end
RSpec has an anonymous controller which comes in handy to test the "base" controller of other controllers, please see this example:
app/controllers/admin/base_controller.rb
class Admin::BaseController < ApplicationController
before_action :authenticate_user!
before_action :admin_required
layout 'admin'
private
def admin_required
render text: 'Unauthorized', status: :unauthorized unless current_user.admin?
end
end
spec/controllers/admin/base_controller_spec.rb
require 'rails_helper'
RSpec.describe Admin::BaseController, :type => :controller do
controller do
def index
head :ok
end
end
describe '#index' do
def do_request
get :index
end
context "as non-admin" do
before { sign_in create(:user) }
it 'raises error' do
do_request
expect(response).to have_http_status(:unauthorized)
expect(response).not_to be_success
end
end
context "as admin" do
before { sign_in create(:user, :with_admin) }
it 'does not raise error' do
do_request
expect(response).to be_success
end
end
end
end
I use a similar structure for my mailers.
My current implementation would need me to add a test to BaseMailer and add corresponding view for that test method.
Is there any way to achieve sort of anonymous mailer testing? something like:
app/mailers/base_mailer.rb
class BaseMailer < ActionMailer::Base
layout 'mailer'
default from: 'Support <support#example.com>',
reply_to: 'Support <support#example.com>',
end
spec/mailers/base_mailer_spec.rb
require 'rails_helper'
RSpec.describe Admin::BaseController, :type => :mailer do
mailer do # <= Anonymous mailer!
def test
mail
end
end
describe '#welcome' do
let(:email) { email_to }
def email_to
mailer.test # <= Anonymous mailer!
end
it { expect(email).to deliver_from 'Support <support#example.com>' }
it { expect(email).to reply_to 'Support <support#example.com>' }
end
end
Then I can get rid of having a test and app/views/base_mailer/test.html.erb file that I'll never used it but just use for testing.
Thanks!
P.S. This mailer testing syntax is from: https://github.com/bmabey/email-spec
Can be achieved, please see this comment:
RSpec.describe BaseMailer do
mailer = Class.new(BaseMailer) do
def a_sample_email
# We need a body to not render views
mail(body: '')
end
end
it 'has the default "from" address' do
email = mailer.a_sample_email
expect(email.from).to eq 'Support <support#example.com>'
end
end
Source: https://github.com/rspec/rspec-rails/issues/1182