What does a bang before a variable do? - ruby-on-rails

I am reading through the RoR guide on testing (http://guides.rubyonrails.org/testing.html)
About 1/5th down the page it says ""To see how a test failure is reported, you can add a failing test to the post_test.rb test case."
test "should not save post without title" do
post = Post.new
assert !post.save
end
I am trying to understand what the use of the bang (!) before post save means.
In an app that I am developing I have a validattion in the model for post
validates :post, presence: true
If I leave the line as it is, the test passes.
If I remove the bang the test fails (because of the validation)
If I move the bang to after the save an exception is raised (because the validation fails
and the bang returns the error message, I think)
So can you help me understand please, what does the bang in front do nd why does it make the test pass?
Question edit / extension: Why is the second test a fail (F) and not an exception (E)

The bang (!) inverts the result of post.save
assert post.save reads assert that the post does save
assert !post.save reads assert that the post doesn't save
Check out the Ruby Logical Operators section at http://www.tutorialspoint.com/ruby/ruby_operators.htm
Edit for extended question:
assert looks for a true result. If the result is true, it returns a pass for that test. If the result is not true, it returns a fail for that test.
Some pseudo code for the asset method,
def assert(result)
if result == true
return 'Pass'
else
return 'Fail'
end
end
assert true
=> 'Pass'
assert false
=> 'Fail'
If there is an exception in the test or code, the test method will rescue the exception and return (E).
Some pseudo code for the test method,
def test(description, &block)
begin
yield
rescue
return 'Exception'
end
end
test 'test description' do
raise 'An error occurred'
end
=> 'Exception'

! is the Logical Not prefix operator - it is not related to variables or methods.
It can be summarized as:
x !x
--------- ---------
nil true
false true
true false
<other> false
(In this case x is the result of evaluating post.save.)

Related

RSpec 3 - raising an error on purpose to test it is rescued

Consider a simple method -
def my_method(users)
eligible_users = []
users.each do |u|
# Go to the next user unless they are eligible
next unless is_eligible?(u)
begin
update_user(u)
eligible_users << u
rescue
puts "Error occured"
# Prints some other stuff about error
next
end
end
end
A key feature of this method is that it loops through users but continues to the next user even if a given user throws an error.
If I were writing spec tests for this, I'd love to pass an array of 3 users and purposely have it error out on the first user. I can then check that the 2nd and 3rd were still correctly processed.
How would I go about raising an error on purpose for only one of the result sets?
I was thinking I could stub is_eligible? and return an error for one of the result and true for the remainder -
allow_any_instance_of(MyClass).to receive(:is_eligible?).and_return(
raise StandardError.new,
true,
true
)
As expected, that doesn't work. Any other approaches?
Thanks!
I can't exactly answer the question, but rather than doing a begin resuce thing,you can follow this approach,
Make the update_user return true or false.
Keep an array of users that falied to update.
return an object like
response: { status: "failure", message: "falied to update #{pluralize(failed_users_array.count, 'user')}", failures: failed_users_array.join(", ") } for failures and
response: { status: "success", message: "#{pluralize(users.count, 'user')} updated successfully" } for all success.
now you can easily test,
have two cases, one where you can test failures and when you can test all success.
Just stub the response object.
For raising errors, you have to do .and_raise("some tezt or StandardError.new") , thats in the docs.

errors.add(:base) works but there is no error message present

I'm trying to create a custom validator and add it's error message to :base. Basically everything is working fine but my message content is not present in object.errors array.
Validator code:
# app/models/video.rb
# ...
validate :if_only_pending_video
# ...
def if_only_pending_video
return unless job_id.present?
if job.videos.pending.any?
errors.add(:base, "My error message")
end
end
Sample output:
FactoryGirl.build(:video).valid? # => false
FactoryGirl.build(:video).errors? # => []
I have about 99% test coverage and i'm sure that valid? returns false in cause of that validator. I just can't understand why there is no message present in errors array.
Looks like the code sample is a bit incorrect by itself. At the first line you build an object and check its validness:
FactoryGirl.build(:video).valid? # => false
The result is false and here the mistake appears: you build a brand new video object and check its errors (but there are none as this object has never been validated yet):
FactoryGirl.build(:video).errors? # => []
# this is a completely different object.
# object_id of this video is not the same as object_id of the first one built.
The way you should check it is to use the same object for validation and for errors checking:
some_video = FactoryGirl.build(:video)
some_video.valid? # => false
some_video.errors # => [[:base, "My error message"]]
As a side note, you could get rid of that conditional inside of if_only_pending_video method and use conditional validation:
validate :if_only_pending_video, :if => lambda{|object| object.job_id.present? }

Continue assertion after failures in ruby

My assertion example is below,
class test < Test::Unit::TestCase
def test_users
begin
assert_equal(user.name, 'John')
assert_equal(user.age, 30)
assert_equal(user.zipcode, 500002)
rescue Exception
raise
end
end
end
If any one of assertions fails, i should move on to process the next one and collect the failure assertions and show failures the end of the result.
I have used add_failure method, its working for looping condition
rescue Test::Unit::AssertionFailedError => e
add_failure(e.message, e.backtrace)
Can any one help ?
A good unit test should test exactly one thing, specifically to avoid problems like you just face. This test case will report on all failed tests, and not just the first failed test:
class MyTest < Test::Unit::TestCase
def test_user_name
assert_equal(user.name, 'John')
end
def test_user_age
assert_equal(user.age, 30)
end
def test_user_zipcode
assert_equal(user.zipcode, 500002)
end
end
Your main problem is that assert_equal ends up calling assert (as shown below) and assert will raise an ArgumentException.
File test/unit/assertions.rb, line 144
def assert_equal(exp, act, msg = nil)
msg = message(msg) {
# omitted code to save space
}
assert(exp == act, msg)
end
File test/unit/assertions.rb, line 29
def assert(test, msg = UNASSIGNED)
case msg
when UNASSIGNED
msg = nil
when String, Proc
else
bt = caller.reject { |s| s.rindex(MINI_DIR, 0) }
raise ArgumentError, "assertion message must be String or Proc, but #{msg.class} was given.", bt
end
super
end
You could extend Test::Unit::Assertions and provide an assertion that does not raise ArgumentError, which is what is stopping continuation past the failed assertion.
See this question for advice on going that direction and adding in safe assertions.
Please find this code for Continue assertion after failures in Ruby :
def raise_and_rescue
begin
puts 'I am before the raise.'
raise 'An error has occured.'
puts 'I am after the raise.'
rescue
puts 'I am rescued.'
end
puts 'I am after the begin block.'
end
Output :
ruby p045handexcp.rb
I am before the raise.
I am rescued.
I am after the begin block.
Exit code: 0

rspec what is the difference between be nil and be_nil

I am using rspec and for asserts like
student.name should be nil
student.name should be_nil
Both seem to work. is there a difference between using be nil an be_nil ???
There is no difference really, except be nil gets defined on the fly, and be_nil has been specifically programmed by rspec.
when you say should.be something, rspec tries the following
[:==, :<, :<=, :>=, :>, :===].each do |operator|
define_method operator do |operand|
BeComparedTo.new(operand, operator)
end
end
Whereas, when you try should.be_nil it just checks
object.nil?
https://github.com/rspec/rspec-expectations/blob/master/lib/rspec/matchers/built_in/be.rb
I think there is no difference but it's used for consistency with other methods like be_true or be_false.
Under the hood be checks the id of both elements:
works with nil
fails with true because in Ruby everything not false nor nil is true
fails with false since both nil and false match

Is there a more rubylike way of doing this helper function

def work_location(application)
if application.contact.work_location.blank? rescue nil
return false
else
return true
end
return false
end
Basically i want to return true or false ....I only want to return true if the work_location is not blank and i need to catch the nil error
Actually this produces a syntax error
syntax error, unexpected modifier_rescue, expecting keyword_then or ';' or '\n'
..._location.blank? rescue nil
def work_location(application)
application.try(:contact).try(:work_location).present?
end
Personally I dislike handling potential nils by doing rescue false because you catch far more than nils: such a rescue rescues all sorts of other errors, for example it will catch NoMethodError, so if you'd typed one of the method names it would squash that error and make it much harder to track down.
Write tests and check both true and false return cases
Shorten code above with:
def work_location(application)
application.contact.work_location.blank? rescue true
end
As far as I can tell, you are creating a helper method here.
I should define a method on application, which you can then use in your views.
The advantage: it is purely object-oriented. An application should know if it has a workplace or not.
Secondly, use try: it will only attempt the given method or block if the receiver is not nil, else it returns nil.
So :
class Application
def has_work_location?
self.contact.try { |c| c.work_location.present? }
end
end
Note that this usage of try only works in rails 3.2, if you are on an older version it does not accept a block. Furthermore nil.present? works and returns falso, so you could write
def has_work_location?
self.contact.try(:work_location).present?
end
Note: because we are adding a method to application, we can safely assume application, so we only need to check that the contact exists anymore.
In your views you can then just write:
<%= #application.contact.workplace if #application.has_work_place? %>
or something similar. Hope this helps.

Resources