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
Related
I am trying to write a simple test to validate updating an Employee and while it works in practice I wanted to write the test anyway.
RSpec.describe EmployeesController, type: :controller do
before(:each) do
admin_user = FactoryBot.create(
:user,
user_type: 1,
email: "admin#admin.com",
password: "oeifhoi2345tf",
password_confirmation: "oeifhoi2345tf"
)
login_as(admin_user)
#employee = create(:employee)
end
it 'expects employee values to update following update' do
p #employee
put :update, params: {
id: #employee[:id],
employee: { name: "New Name" }
}
#employee.reload
p #employee
expect(#employee.name).to eq("New Name")
end
end
The test fails and #employee.name remains unchanged. I have a feeling the update it not even occurring because I added a print line to my controller and I do not see it in my logs:
def update
p "IN UPDATE"
if #employee.update(employee_params)
redirect_to edit_employee_path(#employee[:id])
else
render :edit
end
end
Is there something I am missing in my put call?
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: ... } }
Here is my controller spec
before do
#order = Order.new
end
it "should call find & assign_attributes & test delivery_start methods" do
Order.should_receive(:find).with("1").and_return(#order)
Order.any_instance.should_receive(:assign_attributes).with({"id"=>"1", "cancel_reason" => "random"}).and_return(#order)
Order.any_instance.should_receive(:delivery_start).and_return(Time.now)
post :cancel, order: {id:1, cancel_reason:"random"}
end
The failure is this:
Failure/Error: Unable to find matching line from backtrace
(#<Order:0x007fdcb03836e8>).delivery_start(any args)
expected: 1 time with any arguments
received: 2 times with any arguments
# this backtrace line is ignored
But I'm not sure why delivery_start is being called twice based on this controller action:
def cancel
#order = Order.find(cancel_params[:id])
#order.assign_attributes(cancel_params)
if (#order.delivery_start - Time.now) > 24.hours
if refund
#order.save
flash[:success] = "Your order has been successfully cancelled & refunded"
redirect_to root_path
else
flash[:danger] = "Sorry we could not process your cancellation, please try again"
render nothing: true
end
else
#order.save
flash[:success] = "Your order has been successfully cancelled"
redirect_to root_path
end
end
I would suggest you test the behavior and not the implementation. While there are cases where you would want to stub out the database doing it in a controller spec is not a great idea since you are testing the integration between your controllers and the model layer.
In addition your test is only really testing how your controller does its job - not that its actually being done.
describe SomeController, type: :controller do
let(:order){ Order.create } # use let not ivars.
describe '#cancel' do
let(:valid_params) do
{ order: {id: '123', cancel_reason: "random"} }
end
context 'when refundable' do
before { post :cancel, params }
it 'cancels the order' do
expect(order.reload.cancel_reason).to eq "random"
# although you should have a model method so you can do this:
# expect(order.cancelled?).to be_truthy
end
it 'redirects and notifies the user' do
expect(response).to redirect_to root_path
expect(flash[:success]).to eq 'Your order has been successfully cancelled & refunded'
end
end
end
end
I would suggest more expectations and returning true or false depending on your use. Consider the following changes
class SomeController < ApplicationController
def cancel
...
if refundable?
...
end
end
private
def refundable?
(#order.delivery_start - Time.now) > 24.hours
end
end
# spec/controllers/some_controller_spec.rb
describe SomeController, type: :controller do
describe '#cancel' do
context 'when refundable' do
it 'cancels and refunds order' do
order = double(:order)
params = order: {id: '123', cancel_reason: "random"}
expect(Order).to receive(:find).with('123').and_return(order)
expect(order).to receive(:assign_attributes).with(params[:order]).and_return(order)
expect(controller).to receive(:refundable?).and_return(true)
expect(controller).to receive(:refund).and_return(true)
expect(order).to receive(:save).and_return(true)
post :cancel, params
expect(response).to redirect_to '/your_root_path'
expect(session[:flash]['flashes']).to eq({'success'=>'Your order has been successfully cancelled & refunded'})
expect(assigns(:order)).to eq order
end
end
end
end
Sorry, this is a very unsatisfactory answer, but I restarted my computer and the spec passed...
One thing that has been a nuisance for me before is that I've forgotten to save the code, i.e., the old version of the code the test is running against called delivery_start twice. But in this case, I definitely checked that I had saved. I have no idea why a restart fixed it...
I have test for create:
test "should create article" do
assert_difference('Article.count') do
post :create, article: {title: "Test", body: "Test article."}
end
assert_redirected_to article_path(assigns(:article))
end
I want to do something like this for update action.
My update action looks like:
def update
#article = Article.find(params[:id])
if #article.update(article_params)
redirect_to #article
else
render 'edit'
end
end
I am thinking about something like:
test "should update article" do
patch :update, article {title: "Updated", body: "Updated article."}
end
But I the problems: how to check is my article is updated in Minitest? And how to find item I am going to updated? In fixtures I have two articles.
You should be able to assign one of your fixture articles to a variable and run assertions on the article post-update, something like this (I haven't tested this code, it's just to illustrate the test structure):
test "should update article" do
article = articles(:article_fixture_name)
updated_title = "Updated"
updated_body = "Updated article."
patch :update, article: { id: article.id, title: updated_title, body: updated_body }
assert_equal updated_title, article.title
assert_equal updated_body, article.body
end
You may want to initialize article as an instance variable in your setup method and set it to nil in your teardown method, or however you're managing setup/teardown to make sure your starting state stays consistent from test to 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.