Rails rspec testing a user sending a message to himself/herself - ruby-on-rails

I'm trying to test so a user cannot send a message to himself. Currently on my new message view, I have a select box which gives a selection of all the users in the system except for the current_user. Currently I only have a test which does not allow a user to select himself as the recipient from the select box:
it { should_not have_select(:receiver_id, :options => [user.name]) }
However, is this enough of a test? Do I need to test creating a new message, setting the :receiver_id to the current_user's id and check for it? If so, where would I put this spec, in the model or a request?
Edit (added a validation method in the Message Model, but my rspec passes even if I comment out the validate line):
Edit 2 (The test for the errors hash does not pass):
Message.rb:
validate :validate_sender_receiver
def validate_sender_receiver
if self.receiver_id == self.sender_id
errors.add(:receiver_id, "Cannot send message to self")
end
end
messages_spec.rb
describe "sending message to yourself" do
before do
#message = user.sent_messages.new(:receiver_id => user.id)
end
it "should not be valid" do
#message.should_not be_valid
end
it "should set the error hash" do
#message.errors.should include("Cannot send message to self")
end
end

If a user hacks your select and adds himself to the possible values you might end up with a message that you don't want. I don't know what your controller's action looks like but you should test that in the model and your model should reject the message if the receiver is the same as the sender.

I changed:
it "should set the error hash" do
#message.errors.should include("Cannot send message to self")
end
to:
it "should set the error hash" do
#message.errors.should have_key(:receiver_id)
end
And it now works out well, still don't understand why the first method doesn't work? Does the have_key just check to see if there is a key, but not if it's empty?

Related

Rspec how to checks the value present in the an another method which that method doesn't return anything?

Hi i have written Rspec to validate a body of message\n
describe "validate_msm" do
let!(:user) { FactoryGirl.create(:user) }
it "should contains welcome user" do
body = user.send_sms("909090990")
expect(body).to include("welcome")
end
end
as send_sms will call the send method which i have mentioned in let!
def send_sms
...
..
body="welcome user"
Message.send(contact, body)
end
so how to check with the body content is equal to welcome user ,
as send_sms doesn't return anything, so how to checks the value present in the body variable in rspec
You don't. Or rather you don't that easily. Most of the libraries like this should come together with test adapters and helpers to make such testing possible. If this one does not, you can only test that the message has been sent with correct arguments:
it "should contains welcome user" do
allow(Message).to receive(:send)
user.send_sms("909090990")
expect(Message).to have_received(:send) do |_contact, body|
expect(body).to include "welcome"
end
end

Test failing for feature that works using assert_changes

My chat app has a chat class and a message class; when a message is added to the chat, chat.updated_at should also be updated (achieved with belongs_to :chat, touch: true).
When I test this manually, it works correctly, the time is updated. My test below fails however, and I cannot work out why.
test "sending a message should update chats updated timestamp" do
sign_in #user
assert_changes "#chat.updated_at" do
post messages_path(params: { message: {
text: 'Hello', to_id: #bob.id, chat_id: #chat.id
}})
assert_response :success
end
end
I simply get the error #chat.updated_at didn't change.
My chat fixture is
one:
id: 1
subject: nil
updated_at: <%= 2.hours.ago %>
I think you should use model.reload https://apidock.com/rails/ActiveRecord/Persistence/reload
assert_changes "#chat.reload.updated_at" do
Explanation:
Once a Rails model is loaded from DB, when you access an attribute it will use the values that were already read and not make the same query again and again (unless explicitly told to do so with reload). And in your test, Ruby simply compares #chat.updated_at before and after but there is no second query on the second time, simply a cached attribute
You need to reload the record from the database to get the object in your test to reflect the changes that were performed in the database:
test "sending a message should update chats updated timestamp" do
sign_in #user
assert_changes "#chat.updated_at" do
post messages_path(params: { message: {
text: 'Hello', to_id: #bob.id, chat_id: #chat.id
}})
#chat.reload
assert_response :success
end
end
Remember that #chat in your test and controller point to completely different objects in memory.

How do you trigger the evaluation of a mail method using RSpec?

class MyMailer < BaseMailer
def send_login_prompt
User.reset_login
# ...
end
end
describe MyMailer do
context "when sending a login prompt" do
it "should reset the user's password" do
expect(User).to receive(:reset_login)
MyMailer.send_login_prompt
end
end
end
The incarnation of the test above fails
In the example above we've got a mailer method that triggers a user's password to be
reset. We want to test for that. However when you run the test, .send_login_prompt
does not get called.
In order to actually trigger the code to be evaluated you have to add a line to
inspect the mail object:
Test that works
it "should reset the user's password" do
expect(User).to receive(:reset_login)
mail = MyMailer.send_login_prompt
mail.body # after this line is added the mail is built and the test passes
end
Why is the method not called before and what's the most correct way to prompt the
method to be evaluated rather than the ad-hoc route of calling mail.body?
The mail is not delivered until you access it's content or you can one of the .deliver functions.
This should get the job done nicely
mail = MyMailer.send_login_prompt.deliver_now

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.

Using result of raise_error (capybara)

I'm using capybara to do some web automation.
There are various points in the code where it says things like
raise_error 'Failed as this is a duplicate' if duplicate?
or
raise_error 'Failed to log in' if logged_in? == false
All of this is abstracted to a module and I'd prefer that module to not rely on anything in the models.
What I'm struggling with is how to access that error text when I'm running it, from outside the model.
i.e.
Class Thing
has_many :notes
def do_something
#done = Module::Task.something(self.attribute)
if #done
self.update_attributes(status:'Done')
else
self.notes.new(text: error.text)
end
end
but I can't work out the syntax to get that error text.
Answer: If I understand you correctly then errors that appeared while completing the task
#done = Module::Task.something(self.attribute)
can be accessed via #done.errors.messages
Example: If I have User model where attribute username has 2 validations: presence and format then error messages display like this:
irb(main):019:0* u = User.new
irb(main):022:0* u.save # wont succeed
irb(main):028:0* u.errors.messages
=> {:uid=>["can't be blank", "is invalid"]}
If you want to test error messages with capybara then you can use the syntax like this:
it 'raises jibberishh' do
expect{User.raise_error_method}.to raise_error("jibberishh")
end

Resources