Using result of raise_error (capybara) - ruby-on-rails

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

Related

FactoryGirl attribute set in after(:create) doesnt persist until referenced?

Sorry for the vague title, there are a lot of moving parts to this problem so I think it will only be clear after seeing my code. I'm fairly sure I know what's going on here and am looking for feedback on how to do it differently:
I have a User model that sets a uuid attr via an ActiveRecord callback (this is actually in a "SetsUuid" concern, but the effect is this):
class User < ActiveRecord::Base
before_validation :set_uuid, on: :create
validates :uuid, presence: true, uniqueness: true
private
def set_uuid
self.uuid = SecureRandom.uuid
end
end
I am writing a functional rspec controller test for a "foo/add_user" endpoint. The controller code looks like this (there's some other stuff like error-handling and #foo and #params being set by filters, but you get the point. I know this is all working.)
class FoosController < ApplicationController
def add_user
#foo.users << User.find_by_uuid!(#params[:user_id])
render json: {
status: 'awesome controller great job'
}
end
end
I am writing a functional rspec controller test for the case "foo/add_user adds user to foo". My test looks roughly this (again, leaving stuff out here, but the point should be obvious, and I know it's all working as intended. Also, just to preempt the comments: no, I'm not actually 'hardcoding' the "user-uuid" string value in the test, this is just for the example)
RSpec.describe FoosController, type: :controller do
describe '#add_user' do
it_behaves_like 'has #foo' do
it_behaves_like 'has #params', {user_id: 'user-uuid'} do
context 'user with uuid exists' do
let(:user) { create(:user_with_uuid, uuid: params[:user_id]) } # params is set by the 'has #params' shared_context
it 'adds user with uuid to #foo' do
route.call() # route is defined by a previous let that I truncated from this example code
expect(foo.users).to include(user) # foo is set by the 'has #foo' shared_context
end
end
end
end
end
end
And here is my user factory (I've tried setting the uuid in several different ways, but my problem (that I go into below) is always the same. I think this way (with traits) is the most elegant, so that's what I'm putting here):
FactoryGirl.define do
factory :user do
email { |n| "user-#{n}#example.com" }
first_name 'john'
last_name 'naglick'
phone '718-555-1234'
trait :with_uuid do
after(:create) do |user, eval|
user.update!(uuid: eval.uuid)
end
end
factory :user_with_uuid, traits: [:with_uuid]
end
end
Finally, The problem:
This only works if I reference user.uuid before route.call() in the spec.
As in, if I simply add the line "user.uuid" before route.call(), everything works as intended.
If I don't have that line, the spec fails because the user's uuid doesn't actually get updated by the after(:create) callback in the trait in the factory, and thus the User.find_by_uuid! line in the controller does not find the user.
And just to preempt another comment: I'm NOT asking how to re-write this spec so that it works like I want. I already know a myriad of ways to do this (the easiest and most obvious being to manually update user.uuid in the spec itself and forget about setting the uuid in the factory altogether). The thing I'm asking here is why is factorygirl behaving like this?
I know it has something to do with lazy-attributes (obvious by the fact it magically works if I have a line evaluating user.uuid), but why? And, even better: is there some way I can do what I want here (setting the uuid in the factory) and have everything work like I intend? I think it's a rather elegant looking use of rspec/factorygirl, so I'd really like it to work like this.
Thanks for reading my long question! Very much appreciate any insight
Your issue has less to do with FactoryGirl and more to do with let being lazily evaluated.
From the docs:
Use let to define a memoized helper method. The value will be cached across
multiple calls in the same example but not across examples.
Note that let is lazy-evaluated: it is not evaluated until the first time
the method it defines is invoked. You can use let! to force the method's
invocation before each example.
Since your test doesn't invoke the user object until the expectation there is nothing created. To force rspec to load object, you can use let!.
Instead of using the before_validation callback you should be using after_initialize. That way the callback is fired even before .valid? is called in the model lifecycle.
class User < ActiveRecord::Base
before_initialization :set_uuid!, on: :create, if: :set_uuid?
validates :uuid, presence: true, uniqueness: true
private
def set_uuid!
# we should also check that the UUID
# does not actually previously exist in the DB
begin
self.uuid = SecureRandom.uuid
end while User.where(uuid: self.uuid).any?
end
def set_uuid?
self.uuid.nil?
end
end
Although the chance of generating the same hash twice with SecureRandom.uuid is extremely slim it is possible due to the pigeonhole principle. If you maxed out in the bad luck lottery this would simply generate a new UUID.
Since the callback fires before validation occurs the actual logic here should be completely self contained in the model. Therefore there is no need to setup a callback in FactoryGirl.
Instead you would setup your spec like so:
let!(:user) { create(:user) }
it 'adds user with uuid to #foo' do
post :add_user, user_id: user.uuid, { baz: 3 }
end

Rails rspec testing a user sending a message to himself/herself

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?

Rails Custom Validators: Testing options

I'm trying to write up a rails gem that involves (amongst other things) some custom model validators...and I'm wondering how to test validation options.
To give an example, I'd like to write an rspec test for which a blank field returns valid if the allow_nil option is true, and invalid otherwise. The code works fine, but I can't think of an elegant way to test it. The code itself:
Module ActiveModel
module Validations
module ThirstyVals
class ValidatePrime < EachValidator
# Validate prime numbers
def validate_each(record, attr_name, value)
return if options[:allow_nil] && value.strip.length == 0
# Other validation code here
# ...
end
end
end
end
end
I'm currently testing through a dummy project, which is fine, but the only way I can think of to test the :allow_nil option is to write up a new attribute with :allow_nil set, and verify its functionality...which seems both excessive and pretty inelegant. There must be a more graceful way - any ideas appreciated. (Other tests below for posterity)
# ...
before(:each) do
#entry = Entry.new
end
describe "it should accept valid prime numbers" do
['7', '13', '29'].each do |n|
#entry.ticket = n
#entry.valid?('ticket').should be_true
end
end
describe "it should reject non-prime numbers" do
['4', '16', '33'].each do |n|
#entry.ticket = n
#entry.valid?('ticket').should be_false
end
end
have you considered testing the validator in isolation like so:
in validate_prime_spec.rb
require path_to_validator_file
describe ActiveModel::Validations::ThirstyVals::ValidatePrime do
context :validate_each do
it 'should do some stuff' do
subject.validate_each(record, attr_name, value).should #some expectation
end
end
end
then may I suggest that you need not test the allow_nil functionality of Rails validations due to the fact that it is already tested in Rails? (see: activemodel/test/cases/validations/inclusion_validation_test.rb line 44)

Rails: Test::Unit Why do I need to assert if something is valid or invalid before any other asserts

This seems a little weird, when running a unit test to check if the length of my title is > 10 my test will pass if I include "assert product.invalid?" before any my other assert like this:
require 'test_helper'
class ProductTest < ActiveSupport::TestCase
test "product title is too short" do
product = Product.new(:title => "My lady",
:description => "yyy",
:price => 1
)
assert product.invalid?
assert_equal "must be atleast 10 characters long.", product.errors[:title].join('; ')
end
end
However if I don't include "assert product.invalid?" before assert_equal I get this error
1) Failure: test_product_title_is_too_short blah blah blah
("must be atleast 10 characters long.") expected but was ("").
Is that how Test::Unit works? I have to assert that something is valid or invalid before I proceed with other tests? Kind of like initializing the test?
This isn't actually a characteristic of your test framework, but rather of ActiveRecord.
When creating an object with ActiveRecord, you can assign validations to ensure certain things about the attributes of the objects (as you have on your object). However, these validations only get run at certain times, and as you're observing, the 'new' method isn't one of these times. However, by asking about the validity of the object with the invalid? method, you have thus triggered the validations.
I think what might be more natural is to use the "create" method instead of 'new' to trigger validations for your object. create checks validations automatically which will eliminate your call to "invalid?" in your test and should still populate the errors hash as desired:
product = Product.create(:title => "My lady",
:description => "yyy",
:price => 1
)
assert_equal "must be atleast 10 characters long.", product.errors[:title].join('; ')
Similarly to the 'create' method is the 'create!' method which will actually raise an exception if any validations fail. create will simply return false and populate the error hash.
For more info on validations check out:
http://guides.rubyonrails.org/active_record_validations_callbacks.html
This has nothing to do with Test::Unit. This is rails functionality. The validations only run when either .valid? or one of the versions of .save are called. You can read more about the callback chain here: http://guides.rubyonrails.org/active_record_validations_callbacks.html#when-does-validation-happen

Is shoulda destroying my backtraces?

I have a test more or less like this:
class FormDefinitionTest < ActiveSupport::TestCase
context "a form_definition" do
setup do
#definition = SeedData.form_definition
# ...
I've purposely added a
raise "blah"
somewhere down the road and I get this error:
RuntimeError: blah
test/unit/form_definition_test.rb:79:in `__bind_1290079321_362430'
when I should be getting something along:
/Users/pupeno/projectx/db/seed/sheet_definitions.rb:17:in `sheet_definition': blah (RuntimeError)
from /Users/pupeno/projectx/db/seed/form_definitions.rb:4:in `form_definition'
from /Users/pupeno/projectx/test/unit/form_definition_test.rb:79
Any ideas what is sanitizing/destroying my backtraces? My suspicious is shoulda because the when the exception happens inside a setup or should is whet it happens.
This is a Rails 3 project, in case that's important.
That is because the shoulda method #context is generating code for you. for each #should block it generates a completely separate test for you so e.g.
class FormDefinitionTest < ActiveSupport::TestCase
context "a form_definition" do
setup do
#definition = SeedData.form_definition
end
should "verify some condition" do
assert something
end
should "verify some other condition" do
assert something_else
end
end
end
Then #should will generate two completely independent tests (for the two invocations of #should), one that executes
#definition = SeedData.form_definition
assert something
and another one that executes
#definition = SeedData.form_definition
assert something_else
It is worth noting that it does not generate one single test executing all three steps in a sequence.
These generated blocks of codes have method names like _bind_ something and the generated test have name that is a concatenation of all names of the contexts traversed to the should block plus the string provided by the should block (prefixed with "should "). There is another example in the documentation for shoulda-context.
I think this will give you the backtrace that you want. I haven't tested it, but it should work:
def exclude_backtrace_from_location(location)
begin
yeild
rescue => e
puts "Error of type #{e.class} with message: #{e.to_s}.\nBacktrace:"
back=e.backtrace
back.delete_if {|b| b~=/\A#{location}.+/}
puts back
end
end
exclude_backrace_from_location("test/unit") do
#some shoulda code that raises...
end
Have you checked config/initializers/backtrace_silencers.rb? That is the entry point to customize that behavior. With Rails.backtrace_cleaner.remove_silencers! you can cleanup the silencers stack.
More informations about ActiveSupport::BacktraceCleaner can be found here.

Resources