I'm trying to write a test for a controller. The "create" action expects form data and rejects as "unprocessable entity" if the form data doesn't validate:
Failure:
ContactsControllerTest#test_should_post_create [test/controllers/contacts_controller_test.rb:36]:
Expected response to be a <3XX: redirect>, but was a <422: Unprocessable Entity>
I'm including model, controller, test-controller and fixtures below. Authorization is still out of the box but shouldn't be a factor here (other tests run fine).
I suspect that the foreign keys in the params are the culprit here but I don't know how to check for this. I also tried @saluation.id and @debitor.id in the test params.
Model
class Contact < ApplicationRecord
validates :last_name, presence: true
validates :email, format: {
with: URI::MailTo::EMAIL_REGEXP
}
belongs_to :salutation
belongs_to :debitor
Controller
class ContactsController < ApplicationController
def create
@contact = Contact.new(contact_params)
@debitors = Debitor.all
@salutations = Salutation.all
if @contact.save
redirect_to contacts_path
else
render :new, status: :unprocessable_entity
end
end
private
def contact_params
params.require(:contact).permit(:last_name, :first_name, :email, :salutation_id, :debitor_id)
end
end
Test
require "test_helper"
class ContactsControllerTest < ActionDispatch::IntegrationTest
setup do
@user = users(:lazaro_nixon)
@contact = contacts(:one)
@salutation = salutations(:one)
@debitor = debitors(:one)
end
test "should post create" do
sign_in_as @user
post contacts_url, :params => { :contact => { last_name: "Lastname", first_name: "Firstname", email: "[email protected]", salutation: @salutation, debitor: @debitor }}
assert_response :redirect
follow_redirect!
assert_response :success
end
end
Fixtures - contacts
one:
last_name: MyString
first_name: MyString
email: MyString
salutation: one
debitor: one
Fixtures - salutations
one:
salutation: MyString
Fixtures - debitors
one:
company: MyString
street: MyString
house_number: MyString
zip: 1
city: MyString
payment_terms: one
You need to pass the id of the record and not the entire record and use the same name of the keys as the expected parameter.
require "test_helper"
class ContactsControllerTest < ActionDispatch::IntegrationTest
setup do
@user = users(:lazaro_nixon)
@contact = contacts(:one)
@salutation = salutations(:one)
@debitor = debitors(:one)
end
test "should post create" do
sign_in_as @user
# - don't mix in a bunch of hashrockets
# - use linebreaks for readibility
post contacts_url, params: {
contact: {
last_name: "Lastname",
first_name: "Firstname",
email: "[email protected]",
salutation_id: @salutation.id,
debitor_id: @debitor.id
}
}
assert_response :redirect
follow_redirect!
assert_response :success
end
end
But the test could use a bit of improvement so that it actually tests the important aspects of the controller action.
# be descriptive
test "should create contact with valid params" do
sign_in_as @user
assert_difference ->{ Contact.count }, 1 do
post contacts_url,
params: {
contact: {
last_name: "Lastname",
first_name: "Firstname",
email: "[email protected]",
salutation_id: @salutation.id,
debitor_id: @debitor.id
}
}
end
assert_redirected_to Contact.last
end
This tests that the record is actually created and that the location is the last created record instead of just the controller redirecting you to an arbitrary path which leaves you wide open to false positives.