Rspec ruby rails - ruby-on-rails

I'm trying to get a create action to set up properly.
I keep getting an error: ArgumentError: Unknown keyword: topic
Here is the testing:
require 'rails_helper'
RSpec.describe TopicsController, type: :controller do
let(:my_topic) { Topic.create!(name: RandomData.random_sentence, description: RandomData.random_paragraph)}
describe "POST create" do
it "increases the number of topics by 1" do
expect{ post :create, {topic: {name: RandomData.random_sentence, description: RandomData.random_paragraph}}}.to change(Topic,:count).by(1)
end
it "assigns Topic.last to #topic" do
post :create, { topic: {name: RandomData.random_sentence, description: RandomData.random_paragraph}}
expect(assigns(:topic)).to eq Topic.last
end
it "redirects to the new topic" do
post :create, {topic: {name: RandomData.random_sentence, description: RandomData.random_paragraph}}
expect(response).to redirect_to Topic.last
end
end
Here is the controller:
def create
#topic = Topic.new
#topic.name = params[:topic][:name]
#topic.description = params[:topic][:description]
#topic.public = params[:topic][:public]
if #topic.save
redirect_to #topic, notice: "Topic was saved successfully."
else
flash.now[:alert] = "Error creating topic. Please try again"
render :new
end
end
I'm trying to figure out what I'm missing that is causing this error I've been staring at it for hours and have tried to edit it multiple times to no avail. I can't figure it out. The rest of the project I've been working on has been okay however I cannot figure out why I can't get the word topic to convert successfully. Thanks for taking a look.

Replace :topic with :params. That's the expected keyword for your test. It is already clear to RSpec that you're testing for Topic since your spec file is TopicsController.

The problem is that the post method takes keyword arguments as a second argument.
If you need to specify params, the params keyword should be used:
post :create, params: { topic: { name: ..., description: ... } }

Related

Error occuring in testing my controller with RSPEC using shoulda matchers especially create i can't able to test save functionality

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

Topic.count didn't change by 1

I am testing the create method of my topics controller and keep getting this error:
"Topic.count" didn't change by 1.
Expected: 4
Actual: 3
I have tried a bunch of different solutions from changing the number of attributes that I send in the params to changing what the attributes themselves are, but nothing seems to be working. Any ideas?
Here is my create method:
def create
topic = current_user.topics.create(topic_params)
if topic.save
redirect_to topic
unless current_user.reload.spammer?
AdminMailer.topic_created(topic).deliver_now
end
else
redirect_to topics_path, flash: 'Failed to save this question. Please try again or contact email#domain.com'
end
end
Here is my test:
test "should create topic" do
assert_difference 'Topic.count' do
post topics_url, params: {topic: {title: 'this is a topic', message_attributes: {text: 'this is the question'}, user_id: #user.id, bound_id: #bound.id}}
end
assert_redirected_to topics_url(Topic.last)
end

Testing Rails API controller POST with RSpec

As the title suggests I'm just trying to test the create action in my API controller with RSpec. The controller looks something like:
module Api
module V1
class BathroomController < ApplicationController
skip_before_action :verify_authenticity_token, only: [:create]`
def create
bathroom = Bathroom.new(bathroom_params)
bathroom.user = current_user
if bathroom.save
render json: { status: 'SUCCESS', message: 'Saved new bathroom', bathrooms: bathroom }, status: :ok
end
end
private
def bathroom_params
params.require(:bathroom).permit(:establishment, :address, :city, :state, :zip, :gender, :key_needed, :toilet_quantity)
end
end
end
end
Right now this is doing exactly what it should which is great. The test however...not so much. Here's what I have for the test portion:
describe "POST #create" do
let!(:bath) {{
establishment: "Fake Place",
address: "123 Main St",
city: "Cityton",
state: "NY",
zip: "11111",
gender: "Unisex",
key_needed: false,
toilet_quantity: 1
}}
let!(:params) { {bathroom: bath} }
it "receives bathroom data and creates a new bathroom" do
post :create, params: params
bathroom = Bathroom.last
expect(bathroom.establishment).to eq "Fake Place"
end
end
I'm sure there's more than one thing wrong here but I'm having trouble finding much information about the right way to go about testing this. Any insight or suggestions would be greatly appreciated.
I would skip controller specs altogether. Rails 5 has pretty much delegated ActionController::TestCase (which RSpec wraps as controller specs) to the junk drawer. Controller tests don't send real http requests and stub out key parts of Rails like the router and middleware. Total depreciation and delegation to a separate gem will happen pretty soon.
Instead you want to use a request spec.
RSpec.describe "API V1 Bathrooms", type: 'request' do
describe "POST /api/v1/bathrooms" do
context "with valid parameters" do
let(:valid_params) do
{
bathroom: {
establishment: "Fake Place",
address: "123 Main St",
city: "Cityton",
state: "NY",
zip: "11111",
gender: "Unisex",
key_needed: false,
toilet_quantity: 1
}
}
end
it "creates a new bathroom" do
expect { post "/api/v1/bathrooms", params: valid_params }.to change(Bathroom, :count).by(+1)
expect(response).to have_http_status :created
expect(response.headers['Location']).to eq api_v1_bathroom_url(Bathroom.last)
end
it "creates a bathroom with the correct attributes" do
post "/api/v1/bathrooms", params: valid_params
expect(Bathroom.last).to have_attributes valid_params[:bathroom]
end
end
context "with invalid parameters" do
# testing for validation failures is just as important!
# ...
end
end
end
Also sending a bunch of junk like render json: { status: 'SUCCESS', message: 'Saved new bathroom', bathrooms: bathroom }, status: :ok is an anti-pattern.
In response you should just send a 201 CREATED response with a location header which contains a url to the newly created resource or a response body that contains the newly created resource.
def create
bathroom = current_user.bathrooms.new(bathroom_params)
if bathroom.save
head :created, location: api_v1_bathroom_url(bathroom)
else
head :unprocessable_entity
end
end
If your client can't tell by looking at the response code if the response is successful or not you're doing it wrong.
You don't really need to test the values from the record saved on the database, you could do something like:
expect(post :create, params: params).to change(Bathroom, :count).by(1)
That's enough to test that the create action creates a record on the desired table.
Then you can add more specs to test that Bathroom.new receives the expected parameters (that way you know that it would have those fields when saved), or stub the bathroom object and it's save method to test the response.
If you want to test that the saved record has the right values, I think that spec belongs to the Bathroom model and not the controller (or better, an integration test).
So I followed the advice of max but made one slight change to get it working. My final code was:
RSpec.describe "API V1 Bathrooms", type: 'request' do
describe "POST /api/v1/bathrooms" do
context "with valid parameters" do
let(:valid_params) do
{
bathroom: {
establishment: "Fake Place",
address: "123 Main St",
city: "Cityton",
state: "NY",
zip: "11111",
gender: "Unisex",
key_needed: false,
toilet_quantity: 1
}
}
end
it "creates a new bathroom" do
user = FactoryGirl.create(:user, email: "email1#website.com")
login_as(user, :scope => :user)
expect { post "/api/v1/bathrooms", params: valid_params }.to change(Bathroom, :count).by(+1)
expect(response).to have_http_status :created
expect(response.headers['Location']).to eq api_v1_bathroom_url(Bathroom.last)
end
it "creates a bathroom with the correct attributes" do
user = FactoryGirl.create(:user, email: "email2#website.com")
login_as(user, :scope => :user)
post "/api/v1/bathrooms", params: valid_params
expect(Bathroom.last).to have_attributes valid_params[:bathroom]
end
end
end
end
The key was to use FactoryGirl to create a new user because the bathroom needs an associated user_id to be valid.

Rails joins query -- wrong number of arguments (0 for 1)

I'm having a problem where I am doing a query across tables in Rails, and Rails is returning an error saying that I am sending an argument.
Here's the controller code:
def destroy
#version = Version.where(number: params[:number]).joins(:packages).where(packages: {name: params[:name]}).all
if #version.destroy
render json: {}, status: 204
else
render json: { "error": "Version could not be deleted." }, status: 422
end
end
Here's the failing test:
describe "DELETE #destroy" do
context "valid parameters" do
before { #package = FactoryGirl.create(:package) }
before { #version = FactoryGirl.create(:version) }
it "deletes the version" do
expect {
delete :destroy, format: :json, access_token: #token.token, name: #package.name, number: #version.number
}.to change(Version, :count).by(-1)
end
it "returns 204" do
delete :destroy, format: :json, access_token: #token.token, name: #package.name, number: #version.number
response.status.should eq(204)
end
end
end
Here's the error message:
Failure/Error: delete :destroy, format: :json, access_token: #token.token, name: #package.name, number: #version.number
ArgumentError:
wrong number of arguments (0 for 1)
# ./app/controllers/api/v0/versions_controller.rb:19:in `destroy'
You set #version to be collection (ActiveRecord::Relation) instead of singular record instance. destroy on collections takes argument. If you want to destroy every record in this collection, you can use destroy_all:
#versions.destroy_all
Note: I renamed your instance variable to plural form, so it isn't misleading now.
If you want to find just one Version and destroy it, you can do:
#version = Version.joins(:packages).where(packages: {name: params[:name]}).find_by(number: params[:number])

Table count does not increase after save returns true for nested resource rspec test

I have a 'Mastertag' model as a nested resource for 'Project' with a create action as:
def create
#mastertag = #project.mastertags.build(params[:mastertag])
if #mastertag.save
redirect_to project_mastertags_path, notice: 'Mastertag was successfully created.'
else
render action: "new"
end
end
where #project is initialized in a before filter method.
I have an rspec test as:
describe "POST create" do
context "with valid params" do
it "creates a new Mastertag" do
expect {
post :create, { project_id: #project.id, mastertag: FactoryGirl.attributes_for(:mastertag_without_project) }
}.to change(Mastertag, :count).by(1)
end
end
When I run the test, the #mastertag.save method returns true however the count still remains the same. The test hence fails. This looks pretty strange. Where am I going wrong?
As I was using Mongoid and 'Mastertags' was embedded into Project, there is no separate collection for Mastertags.
I had to change the code to :
describe "POST create" do
context "with valid params" do
it "creates a new Mastertag" do
expect {
post :create, { project_id: #project.id, mastertag: FactoryGirl.attributes_for(:mastertag_without_project) }
}.to change {#project.reload.mastertags.count}.by(1)
end
end
I got help from this Stackoverflow question : RSpec/Mongoid: Expect to change count on embedded models
Check your project_mastertags_path and make sure the redirect after if #mastertag.save works.
also, try replacing if #mastertag.save with if #project.save in your create method.

Resources