How to Test 'Create' Controller Actions

What is the proper way to test 'create' controller actions?

How about:

it "creates article" do 
article_params = FactoryGirl.attributes_for(:article)
expect { post :create, :article => article_params }.to change(Article, :count).by(1)
end

How to call the create action from the controller in RSpec

Request specs are integration tests, using something like Capybara to visit pages as a user might and perform actions. You wouldn't test a create action from a request spec at all. You'd visit the new item path, fill in the form, hit the Submit button, and then confirm that an object was created. Take a look at the Railscast on request specs for a great example.

If you want to test the create action, use a controller spec. Incorporating FactoryGirl, that would look like this:

it "creates a post" do
post_attributes = FactoryGirl.attributes_for(:post)
post :create, post: post_attributes
response.should redirect_to(root_path)
Post.last.some_attribute.should == post_attributes[:some_attribute]
# more lines like above, or just remove `:id` from
# `Post.last.attributes` and compare the hashes.
end

it "displays new on create failure" do
post :create, post: { some_attribute: "some value that doesn't save" }
response.should redirect_to(new_post_path)
flash[:error].should include("some error message")
end

These are the only tests you really need related to creation. In your specific example, I'd add a third test (again, controller test) to ensure that the appropriate DifferentModel record is created.

Rspec testing controller action create logic

In both cases, when a customer is found or a new customer is created, the response will be a redirect_to and this is why your tests are passing. Note that since you are using build in your test, this is not saving the customer in the database and therefore your test is always creating a new customer and Customer.find_by(phone: customer_params[:phone]) is always evaluating to nil.

The best way to test your create action is by comparing the count of Customer instances before and after the request is made. Here is a how you can improve upon your tests.

let(:customer) { FactoryGirl.create(:customer, phone: '999') }

context 'user not found by phone' do
it 'creates a new customer' do
expect{
post :create, customer: attributes_for(:customer, phone: "999")
}.to change(Customer, :count).by 1
end

it 'redirects to the customer path' do
post :create, customer: attributes_for(:customer, phone: "999")

customer = Customer.last
expect(response).to redirect_to(customer)

#this is another differentiator between the case where a customer is
#found and when one is created
expect(flash[:notice).to eq "Customer was successfully saved"
end
end

context 'user not found by phone' do
before(:each) do
#here we create the customer before each test in this context
customer
end

it 'does not create a new customer' do
expect{
post :create, customer: attributes_for(:customer, phone: customer.phone)
}.not_to change(Customer, :count)
end

it 'redirects to the customer path' do
post :create, customer: attributes_for(:customer, phone: customer.phone)

customer = Customer.last
expect(response).to redirect_to(customer)

#this is another differentiator between the case where a customer is
#found and when one is created
expect(flash[:notice).to be_nil
end
end

What exactly should I test for a controller action method that returns a View with ViewModel

It depends on what your controller action is supposed to do, but you should for example verify that the right view was returned, whether the right view model was returned and assert the expected state of the view model. Please refer to the docs for more information about how to unit test controllers:

https://learn.microsoft.com/en-us/aspnet/mvc/overview/older-versions-1/unit-testing/creating-unit-tests-for-asp-net-mvc-applications-cs

https://learn.microsoft.com/en-us/aspnet/core/mvc/controllers/testing?view=aspnetcore-2.1

Keep in mind that the controller logic should be minimal though. Your business logic should be implemented in the model(s).

RSpec test controller action create not affect test database

It's because you have this line:

config.use_transactional_fixtures = true

The name of this setting is a bit misleading. What it really means in
Rails is "run every test method within a transaction." In the context
of rspec-rails, it means "run every example within a transaction."

The idea is to start each example with a clean database, create
whatever data is necessary for that example, and then remove that data
by simply rolling back the transaction at the end of the example.

See: https://relishapp.com/rspec/rspec-rails/docs/transactions

RSpec test for create action of a controller for a nested resource

For a nested resource you need to construct the setup data and the post in such a way as to identify the parent article when posting the child comment.

One approach is to setup Factory Girl associations correctly and then ensure the parent element is set when creating the child attributes. It would look something like this:

In the comment factory:

FactoryGirl.define do
Factory :comment do
comment "My comment"
article
end
end

By calling article, and making sure that there is a valid factory called :article then FactoryGirl will create an article when a comment is created. To make the tests flow well we should actually be specific about which article is used when the comment is created, so now that the Factory is in place we use the following in the spec.

@comment_attributes = FactoryGirl.attributes_for(:comment, :article_id => @article)

This will build comment attributes which are automatically attached to @article. The final piece is then to construct the post, making sure that we include the parent and the child.

When a nested resource is posted it expects params for both the parent resource and the child. In rspec we can provide this in the post as follows:

post :create, :article_id => @article, :comment => @comment_attributes

This should link up all the pieces correctly.

Testing create Action using RSpec in Rails

[For Future Reader] I got it to work by doing the fooling in leave_controller_spec.rb file.

 describe 'POST#create' do

context 'with valid attributes' do
let(:valid_attribute) do
attributes_for(:leave,
user_id: 2,
team_lead_id: 3,
fiscal_year_id: 2,
start_day: '2018-10-10'.to_date,
end_day: '2018-10-10'.to_date,
reason: 'Sick',
status: 2)
end

it 'saves the new leave in the database' do

expect do
post :create, params: {leave: valid_attribute}
end.to change(Leave,:count).by(1)
end
it 'redirects to leave#index' do
render_template :index
end
end


Related Topics



Leave a reply



Submit