Issues with testing in rails - ruby-on-rails

I am writing tests for my application. I have written quite a few tests and they were working fine. However, two of my tests keep failing and I am not sure why that is. The first test is a unsuccessful editing of a form.
The test is given below
test "unsuccessfull editing of the scoreboard" do
log_in_as(#user)
get edit_scoreboard_path(#scoreboard)
assert_template 'scoreboards/edit' (doesn't work)
patch scoreboard_path(#scoreboard), scoreboard: { name_of_scoreboard: "a"* 51,
name_of_organization: "b"*60,
name_of_activity: "c"*60 }
assert_template 'scoreboard/edit' (doesn't work)
end
The error associated with this test is given below.
ScoreboardEditTest#test_unsuccessfull_editing_of_the_scoreboard [/home/ubuntu/workspace/test/integration/scoreboard_edit_test.rb:13]:
expecting <"scoreboards/edit"> but rendering with <[]>
The controller for the following test is also given below.
def update
#scoreboard = Scoreboard.find(params[:id])
if #scoreboard.update_attributes(scoreboard_params)
redirect_to #scoreboard
flash[:success] = "Updated Successfully"
else
render 'edit'
end
end
After you get the edit_scoreboard_path, the edit scoreboard template should show up. I am not exactly sure why it gives me the error. I have the exact same thing with the user model and its working just fine. I think I am missing something in my understanding of it works.
The second test is a valid creation of a scoreboard. The test is given below.
test "valid creation of the scoreboard with passing validations" do
log_in_as(#user)
get new_scoreboard_path
assert_difference 'Scoreboard.count', 1 do
post scoreboards_path, scoreboard: {name_of_scoreboard: "abc",
name_of_organization: "def",
name_of_activity: "ghi" }
end
assert_redirected_to scoreboard_path(#scoreboard) (doesn't work)
assert_equal 'Scoreboard created successfully', flash[:success]
end
It redirecting to the wrong scoreboard id. In the fixtures I have the id set as 1. The error message is given below.
Expected response to be a redirect to <http://www.example.com/scoreboards/1> but was a redirect to <http://www.example.com/scoreboards/941832920>.
I am not sure exactly what this means. As I mentioned, I have the ID set in the fixtures. I even manually set the id to '941832920'. It still gave me an error. Not sure why its doing that.

For your 2nd test, I don't think you can set an id, it gets assigned by the database when the record is saved. A better way to check whether you get directed to the correct scoreboard path would be the preferred format: assert_redirected_to scoreboard_path(assigns(:scoreboard)).

Related

Using Rails 5 and minitest, how can I access a record that was created in the test?

I have this test that was passing before I switched the Note table to use UUID:
test "SHOULD create note AND redirect to Notes#index(SINCE null folder_id) WHEN only body is provided IF logged in as account:owner" do
sign_in #user
assert_difference('#user.account.notes.count') do
post notes_url(#user.account.hash_id), params: { note: { body: #note_by_user_no_folder.body } }
end
assert_redirected_to notes_url(#user.account.hash_id, anchor: Note.last.id)
end
But after switching to UUID, I'm getting the following failure error:
Failure:
NotesControllerTest#test_SHOULD_create_note_AND_redirect_to_Notes#index(SINCE_null_folder_id)_WHEN_only_body_is_provided_IF_logged_in_as_account:owner [/Users/chris/Dropbox/Repositories/keepshelf/test/controllers/notes_controller_test.rb:117]:
Expected response to be a redirect to <http://www.example.com/11111111/notes#9dc409ff-14cc-5f64-8f5f-08e487f583ee> but was a redirect to <http://www.example.com/11111111/notes#34dac6b7-46af-4c5c-bff7-760ffa77edf6>.
Expected "http://www.example.com/11111111/notes#9dc409ff-14cc-5f64-8f5f-08e487f583ee" to be === "http://www.example.com/11111111/notes#34dac6b7-46af-4c5c-bff7-760ffa77edf6".
I take this as that since the UUID's do not follow an order, that the new Note I am creating in the "post notes_url(..." does not end up being the "last" one that Note.last.id finds.
How can I set the anchor: to record_that_was_just_created.id ?
I figured this out. apparently there is a way to access the controller's instance variables in the test so I got it to work by changing the assert_redirected_to to
assert_redirected_to notes_url(#user.account.hash_id, anchor: controller.instance_variable_get(:#note).id)
Late answer here, I'm glad you got the result you need!
... in case anyone else reads this looking for the test practices ...
The short answer is - you should always know what data is being used in the test ... because you make it. The most common 3 ways ...
In the setup method where you
Load a fixture #account = Accounts(:first) (needs matching entry in yaml file)
Call factorybot gem (see thoughtbot's page for how to)
Explicitly write #account = #user.account.build(field: value, field2: value)
So, you have the literal answer to your question - it's bad practice to test against uncertain data - Shaunak was saying that too.
The above is probably enough - but your detail oriented testing should be in unit tests - where you are not calling against db & making your tests the slowest possible version. Your integration level stuff should just test that creation was successful - you already know the data going in there works.
For the best experience you probably want unit tests something from the 3 methods above in setup & use #account in the test like so to test against the validations you are about to create ...
So the unit test would be ...
setup
#account = #user.account.build # avoid hitting db unnecessarily versus create
#account.field = value
#account.field2 = value2
end
def test_details_pass_activerecord_validations
signin #user
#account.test_specific_fields = value # stuff not all the tests use
assert #account.valid? # trigger all the activerecord validations, but not db
end
From there your integration ...
test "SHOULD create note AND redirect to Notes#index(SINCE null folder_id) WHEN only body is provided IF logged in as account:owner" do
# Your setup method above should have most of the intialization
# ... basically #account = #user.account .note would be setup, but not inserted
signin #user
assert_difference('#user.account.notes.count') do
post notes_url(#account), params: { note: { body: #account.note.body } }
end
assert_redirected_to notes_url(#account, anchor: #account.whatever_field)
end

Rails - Why am I getting 'You are being redirected' in integration test?

When writing integration tests for a Rails 5 application I encountered the infamous 'You are being redirected' page for no apparent to me reasons. There are two highly similar tests:
test "GETtting correct activation link on an already activated user gives error message and redirects to root url" do
# GIVEN a non-yet-registered email address
email_address = "tester#testing.net"
# GIVEN the sign-up page has been displayd
get signup_path
# GIVEN new user is created
post signup_path, params: { user: { email: email_address, email_confirmation: email_address, password: "testpassword", password_confirmation: "testpassword" } }
# GIVEN the URI from activation email
activation_uri = URI.extract(ActionMailer::Base.deliveries.last.text_part.body.encoded)[0]
# GIVEN the URI's been used and the user is already activated
get activation_uri
# WHEN reading back the newly activated user
activated_user = User.find_by_email(email_address)
# EXPECT the user to be activated
assert activated_user.activated?
# WHEN using the activation link on an already activated user
get activation_uri
# EXPECT redirection to root path
assert_redirected_to root_url
follow_redirect!
# EXPECT flash message
assert_not flash.empty?
# EXPECT rendered page to contain activation error information
assert_select 'div#flash div h5', text: I18n.translate('users.activate.error')
end
which finishes correctly, and the next one:
test "GETtting incorrect activation hash on a non-activated user gives error message and redirects to root url" do
# GIVEN a non-yet-registered email address
email_address = "tester#testing.net"
# GIVEN the sign-up page has been displayd
get signup_path
# GIVEN new user is created
post signup_path, params: { user: { email: email_address, email_confirmation: email_address, password: "testpassword", password_confirmation: "testpassword" } }
# WEHN GETting the activation URI with invalid activation hash
activation_uri = "http://localhost:3000/account_activations/waTbfcCoZoPTBEIcewsl8Q/edit?email=#{ERB::Util.url_encode(email_address)}"
get activation_uri
# EXPECT redirection to root path
assert_redirected_to root_url
follow_redirect!
# EXPECT flash message
assert_not flash.empty?
# EXPECT rendered page to contain activation error information ('You are being redirected' rendered here)
assert_select 'div#flash div h5', text: I18n.translate('users.activate.error')
end
which fails miserably on the last assert because 'You are being redirected' is being rendered instead of the page I am expecting to be rendered. In both cases I use follow_redirect! and the first one works, while the second one doesn't. The static URL in the second test is correct. It only uses a valid but non-associated hash instead of the expected one. In the controller there is simple
flash[:error] = "#{t'users.activate.error'}"
redirect_to root_url
in both cases (the same method). I receive proper 302 response code and proper redirection URL. When doing the same tests manually in the browser, correct page is rendered. When running the tests I get 'You are being…' in the second test.
Any clues?
I think it's happen due to authorisation problem, you are calling a method with the data that does'not have access to that method, and it redirect it to somewhere. I have faced similar problem when i was trying to redirect with status code 401(unauthorised)
Did you try, puts response.body in there to see whats going on?
There's a blurp in hartl's rails tutorial that helped me during a similar situation ... I applied his trick in a similar devise situation, honestly I'm not completely sure without poking around your code that the redirect is from a bad URI or not - but thought I'd leave this here for anyone else with issues. I stumbled your post when looking for some answers to devise redirects myself (though mine are probably related to ID not found issues).
When I originally wrote this chapter, I couldn’t recall offhand how to escape URLs in Rails, and figuring it out was pure technical sophistication (Box 1.1). What I did was Google “ruby rails escape url”, which led me to find two main possibilities, URI.encode(str) and CGI.escape(str). Trying them both revealed that the latter works. (It turns out there’s a third possibility: the ERB::Util library supplies a url_encode method that has the same effect.)

How to test a class method in rspec that returns a model instance

I'm trying to test my User model's class method #registered_but_not_logged_in(email), which grabs the first user that matches the email that has a confirmed_at entry but has never logged in (which I'm counting with sign_in_count). I'm using rspec with Factorygirl, plus shoulda-matchers v2.8.
Here's the ruby:
def self.registered_but_not_logged_in email
self.with_email( email ).confirmed.never_signed_in.first
end
I've tested this in the Rails console and I know it works as expected so it's not a logic problem on that end, so I'm thinking I'm doing something wrong in my test:
describe User do
# create #user
describe ".registered_but_not_logged_in" do
it "returns a user that matches the provided email who is confirmed, but who has not yet signed in" do
#user.confirmed_at = 2.days.ago
#user.email = "fisterroboto5893#mailinator.com"
result = described_class.registered_but_not_logged_in("fisterroboto5893#mailinator.com")
expect(result).to be_instance_of(User)
end
end
In this example, result is nil. I know that this is a case of #user existing outside the database while the method is actively checking the DB, but I don't know how to handle this while using rspec/factorygirl. Any help is definitely appreciated!
So I'm not 100% sure why what I'm doing is working, but here's the solution that I stumbled across with the help of #nort and one of my coworkers:
it "returns a user that matches the provided email who is confirmed, but who has not yet signed in" do
#user.confirmed_at = 2.days.ago
#user.email = "fisterroboto5893#mailinator.com"
#user.sign_in_count = 0
#user.save!
expect(User.registered_but_not_logged("fisterroboto5893#mailinator.com")).to be_instance_of(User)
end
What I believe is happening is the save! is saving #user to the test database, which is otherwise completely unpopulated as I develop against a different DB. The issue of course being that we can't test data that doesn't exist.
As a bonus, note that expect().to... is the preferred convention for rpsec. Also, described_class I believe would totally work fine, but am preferring explicitness right now since I'm still learning this stuff.

How do I check for an existing user in Rspec on a Post#Create?

I am trying to write a test for my InvitationsController#Create.
This is a POST http action.
Basically what should happen is, once the post#create is first executed, the first thing that needs to do is we need to check to see if an User exists in the system for the email passed in via params[:email] on the Post request.
I am having a hard time wrapping my head around how I do this.
I will refactor later, but first I want to get the test functionality working.
This is what I have:
describe 'POST #create' do
context 'when invited user IS an existing user' do
before :each do
#users = [
attributes_for(:user),
attributes_for(:user),
attributes_for(:user)
]
end
it 'correctly finds User record of invited user' do
expect {
post :create, invitation: attributes_for(:member, email: #users.first.email)
}.to include(#users.first[:email])
end
end
end
This is the error I get:
1) Users::InvitationsController POST #create when invited user IS an existing user correctly finds User record of invited user
Failure/Error: expect {
You must pass an argument rather than a block to use the provided matcher (include "valentin#parisian.org"), or the matcher must implement `supports_block_expectations?`.
# ./spec/controllers/users/invitations_controller_spec.rb:17:in `block (4 levels) in <top (required)>'
I am not surprised by the error, because the Test doesn't feel right to me. I just can't quite figure out how to test for this without writing code in my controller#action.
I am using FactoryGirl and it works perfectly, in the sense that it returns valid data for all the data-types. The issue here is how do I get RSpec to actually test for the functionality I need.
The error you are getting is a syntax error, nothing related to whatever your action is supposed to do.
The code you have there it is being interpreted as you are passing a block ({}) to the expect method.
I'd change it to something like
it 'correctly finds User record of invited user' do
post :create, { email: #users.first[:email] }
expect(response).to include(#users.first[:email])
end
Assuming that the response of the create action returns the email as plain text, which seems weird to me.
Also note that I have email directly passed to the post since you mentioned you were expecting it in params[:email] but by the test you wrote seems like you were expecting it in params[:invitation][:email].
Change that part if that is the case.

Reloading an object not working in rspec

I am trying to test a controller method with the following code:
it "should set an approved_at date and email the campaign's client" do
#campaign = Campaign.create(valid_attributes)
post :approve, id: #campaign.id.to_s
#campaign.reload
#campaign.approved_at.should_not be(nil)
end
However, when I run this test, I get the following error:
Failure/Error: #campaign.reload
ActiveRecord::RecordNotFound:
Couldn't find Campaign without an ID
When I run the analagous lines in the rails console, the reload works and the value is set as I need it to be. Why isn't reload working for me when I run the code in an rspec test?
I solved the problem by switching to FactoryGirl:
#campaign = FactoryGirl.create(:pending_approval_campaign)
#campaign.approved_at.should be(nil)
post :approve, id: #campaign.id.to_s
#campaign.reload
#campaign.approved_at.should_not be(nil)
That works as intended
Two possible places for errors.
object creation. i.e.#campaign = Campaign.create(valid_attributes) Your object may not be created correctly. I suggest you to use create! instead of create in the test so that any error will be thrown.
Controller. When controller expect to find the object with an integer id, you feed it a string. That may also be the problem. I suggest you not to convert the id into string. If for GET, you can do that though not necessary. If for POST, converting to string is wrong.
I would run a test to ensure a Campaign record is actually being created:
#campaign = Campaign.create(valid_attributes)
puts #campaign.id
.reload is the first place in your code that a nil #campaign would flag an error (since you can call .to_s on a nil object)

Resources