RSpec: Assert successful create request without assigns method - ruby-on-rails

I have this code in a create method inside a Rails controller:
if #product.save
format.html { redirect_to #product, notice: 'Product was successfully created.' }
To test this code, I have this expectation in an RSpec file:
expect(response).to redirect_to(assigns(:product))
Using assigns is deprecated/has been moved to a gem and frankly I don't care if #product or #my_product has been created in the controller. Actually I just want to know if I have been redirected to /products/<some-id>. Is there a (recommended) way to do so?

If you want render new you you'll need add gem 'rails-controller-testing' to your Gemfile.
After read your comments i guess your action #create is look like that:
def create
#product = Product.new(product_params)
respond_to do |format|
if #product.save
format.html { redirect_to #product, notice: 'Product was successfully created.' }
format.json { render :show, status: :created, location: #product }
else
format.html { render :new }
format.json { render json: #product.errors, status: :unprocessable_entity }
end
end
end
You could do a test like that:
describe 'POST /products' do
context 'when everithing is ok' do
it 'returns the product' do
post products_url, params: { product: { description: 'lorem ipsum', title: 'lorem ipsum' } }
expect(response).to redirect_to(product_url(Product.last))
end
end
context 'when something worong' do
it 'redirect to new' do
post products_url, params: { product: { description: 'lorem ipsum' } }
expect(response).to render_template(:new)
end
end
end

This GitHub Issue explains why assigns is deprecated
Testing what instance variables are set by your controller is a bad idea. That's grossly overstepping the boundaries of what the test should know about. You can test what cookies are set, what HTTP code is returned, how the view looks, or what mutations happened to the DB, but testing the innards of the controller is just not a good idea.
you can test the response status code using have_http_status matcher
expect(response).to have_http_status(:success)

Related

Rails 5 Failing Test ActiveRecord::AssociationTypeMismatch

I'm currently working through Agile Development with Rails 5 and have run into an issue with my testing. Namely, there is an association mismatch going on where it expects the Product but gets a String. I've tried fixing the line in my line_items_controller_test.rb from #line_item.product to #line_item.product_id to no avail and have poked around as much as I know how.
Here is my test file line_items_controller_test.rb
test "should update line_item" do
patch line_item_url(#line_item), params: { line_item: { cart_id: #line_item.cart_id, product: #line_item.product } }
assert_redirected_to line_item_url(#line_item)
end
and my test failure from the terminal
Error:
LineItemsControllerTest#test_should_update_line_item:
ActiveRecord::AssociationTypeMismatch: Product(#70143083725900) expected, got String(#70143078473840)
app/controllers/line_items_controller.rb:47:in `block in update'
app/controllers/line_items_controller.rb:46:in `update'
test/controllers/line_items_controller_test.rb:40:in `block in <class:LineItemsControllerTest>'
bin/rails test test/controllers/line_items_controller_test.rb:39
and here is the controller itself where it mentions 47 and 46 beginning with respond_to
def update
respond_to do |format|
if #line_item.update(line_item_params)
format.html { redirect_to #line_item, notice: 'Line item was successfully updated.' }
format.json { render :show, status: :ok, location: #line_item }
else
format.html { render :edit }
format.json { render json: #line_item.errors, status: :unprocessable_entity }
end
end
end
Let me know if there's any more info you need. You can also check my repo at https://github.com/jamesemcc/depot
Ok. There are problems not only in your test, but in app.
https://github.com/jamesemcc/depot/blob/master/app/controllers/line_items_controller.rb#L75 You should permit product_id, not product here. You cannot pass Product object from browser.
You should pass product_id from view and from test
Good Luck!

rails controller test failing non-deterministicly wrt state leak (I think)

I have a standard rest controller (with load_and_authorize_resource) for which I have the following strong params allowed:
def subscription_params
params.require(:subscription).permit(:email,:confirmed)
end
update action:
def update
respond_to do |format|
if #subscription.update(subscription_params)
format.html { redirect_to #subscription, notice: 'Subscription was successfully updated.' }
format.json { render :show, status: :ok, location: #subscription }
else
format.html { render :edit }
format.json { render json: #subscription.errors, status: :unprocessable_entity }
end
end
end
In my test I have:
test "registered can edit confirmed" do
u = users(:registered)
sign_in u
#subscription = u.subscription
new_value = !#subscription.confirmed
patch :update, id: #subscription, subscription: { confirmed: new_value, email: #subscription.email, token: #subscription.token }
assert_response :redirect
assert_equal new_value, u.subscription.confirmed
assert_redirected_to subscription_path(#subscription)
assert_includes flash[:notice], "Subscription was successfully updated."
end
which fails non-deterministically (the confirmed field isn't updated). I don't know enough about rails' ecosystem to know where the problem is. I'm using devise and cancancan.
If I remove config.active_support.test_order = :random the test fails every time. And If I run the tests that fail on their own they always pass. Which lead me to believe that state is leaking between tests and causing issues but I can't figure out what.
Turns out I needed to call #subscription.reload.

How to test database failure with Minitest?

I'm targetting 100% coverage on a fresh new rails application. I only have one resource, auto-generated.
For creation and update operations, the generated source code contains a "else" case, triggered on database failure (save has failed).
# POST /projects
# POST /projects.json
def create
#project = Project.new(project_params)
#project.user = current_user
respond_to do |format|
if #project.save
format.html { redirect_to projects_url, notice: 'Project was successfully created.' }
format.json { render :show, status: :created, location: #project }
else
format.html { render :new }
format.json { render json: #project.errors, status: :unprocessable_entity }
end
end
end
As It's part of my controller (and may include dedicated behavior like redirection or error message), I want to test it in unit test, under MiniTest.
I have a beginning of solution with creating a test containing ActiveRecord::Base.remove_connection. I also create a teardown function containing the opposite : ActiveRecord::Base.estabish_connection
test "should not create project" do
assert_equal('Project.count') do
ActiveRecord::Base.remove_connection
post :create, project: { name: #project.name, user_id: #project.user_id }
end
assert_redirected_to projects_path
end
And to restore connection for other tests :
teardown do
ActiveRecord::Base.establish_connection
end
It's not acceptable for (at least) two reasons :
the test finishes in a exception ConnectionNotEstablished which is not the goal of the test, and after that I can't verify the behavior of the controler itself (redirection)
establish_connection is performed on every test (useless 90% of time)
Thanks for your suggestions !
EDIT : added code source
As I mentioned in my comment, I think you want to use a mock instead of disconnecting your database. Since you are using #project.save to check if the save worked, we want it to return false instead of raising a ConnectionNotEstablished error.
We can try to use MiniTest::Mock to accomplish this, but other people might use an external mocking library like Mocha.
Note: I don't use minitest, so this is my best guess
test "should not create project" do
Project.expect(:find, #project) do
#project.expect(:save, false) do
assert_equal('Project.count') do
post :create, project: { name: #project.name, user_id: #project.user_id }
end
end
end
assert_redirected_to projects_path
end
If you use mocha, it might look something like this:
test "should not create project" do
Project.any_instance.stubs(:save).returns(false)
assert_equal('Project.count') do
post :create, project: { name: #project.name, user_id: #project.user_id }
end
assert_redirected_to projects_path
end

How to send params with FactoryGirl (as opposed to manually sending the params as a hash)?

I have the following rspec test that works:
it "redirects to the created api_key" do
post :create, :api_key => {:api_identifier => "asdfadsf", :verification_code =>
"12345"}
response.should redirect_to(ApiKey.last) #(or any other test function)
end
But I use Factory girl so I don't have to manually create api_keys.
How can I replicate the above functionality, but use factory girl?
Using:
it "redirects to the created api_key" do
test = FactoryGirl.build(:api_key)
post :create, :api_key => test
response.should redirect_to(ApiKey.last) #(or any other test function)
end
or:
it "redirects to the created api_key" do
post :create, FactoryGirl.build(:api_key)
response.should redirect_to(ApiKey.last) #(or any other test function)
end
Gives me null values for the :api_key value when I arrive at my controller.
For reference, here is my create action that this test is testing:
def create
#api_key = ApiKey.new(params[:api_key])
#api_key.user = current_user
pp #api_key
respond_to do |format|
if #api_key.save
format.html { redirect_to #api_key, notice: 'Api key was successfully created.' }
format.json { render json: #api_key, status: :created, location: #api_key }
else
format.html { render action: "new" }
format.json { render json: #api_key.errors, status: :unprocessable_entity }
end
end
end
try:
post :create, :api_key => FactoryGirl.attributes_for(:api_key)
Using build doesn't actually create a record. It just pretends it did. Using attributes_for will give you the attributes of an object. This is mostly used in the context you describe. Note that this too will not create an object.
What I would do is this if the response is successful/redirect:
response.should be_redirect
Or even better use expect.

post functional test failing

I am trying to create a functional test that tests the create method in one of my controllers. For the life of me, I cannot figure out why this is failing. I am getting one failure, and zero errors:
1) Failure:
test_should_create_order(OrdersControllerTest) [/Users/user/rails_work/depot/test/functional/orders_controller_test.rb:38]:
"Order.count" didn't change by 1.
<3> expected but was
<2>.
So, Im pretty sure this means that my functionals test was unable to make an Order. Here is my test:
setup do
#order = orders(:one)
end
test "should create order" do
assert_difference('Order.count') do
post :create, order: #order.attributes.slice(Order.accessible_attributes)
end
assert_redirected_to store_url
end
my orders fixture:
one:
name: Dave Thomas
address: MyText
email: dave#example.org
pay_type: Check
and my Order#create controller:
def create
#order = Order.new(params[:order])
#order.add_line_items_from_cart(current_cart)
respond_to do |format|
if #order.save
Cart.destroy(session[:cart_id])
session[:cart_id] = nil
format.html { redirect_to store_url, notice: 'Thank you for your order' }
format.json { render json: #order, status: :created, location: #order }
else
#cart = current_cart
format.html { render action: "new" }
format.json { render json: #order.errors, status: :unprocessable_entity }
end
end
end
Now, if I change the setup method in my functional test to:
#order = Order.create(orders(:one))
Instead of:
#order = orders(:one)
The failure disappears, but I get about 8 of these errors:
NoMethodError: undefined method `stringify_keys' for #<Order:0x007f8c62dbb960>
If anyone can help me fix this functional test, I would more than appreciate it. Any and all input is welcome.
Bottom line: assign each order attribute individually.
I'm not familiar with the 'mass assignment' vulnerability (new to Rails), but here (Pragmatic Forums) is a case of someone having difficulty with that specific test because of it.
Try spelling out each attribute of the order individually. Instead of
post :create, order: #order.attributes.slice(Order.accessible_attributes)
use
post :create, order: {
address: #order.address,
email: #order.email,
name: #order.name,
pay_type: #order.pay_type
}
The test as a whole will be this:
test "should create order" do
assert_difference('Order.count') do
post :create, order: { address: #order.address, email: #order.email, name: #order.name, pay_type: #order.pay_type }
end
assert_redirected_to store_path
end

Resources