Why is negative specific exception expectation deprecated in RSpec? - ruby

Seems like I am permitted to specify exception class for .to but not for .not_to?
What are exact reasons of this?
Failure/Error: expect{ smth }.not_to raise_exception SomeExceptionClass
ArgumentError:
`expect { }.not_to raise_error(SpecificErrorClass)` is not valid, use `expect { }.not_to raise_error` (with no args) instead

To expand on Nakilon's answer a little:
This is a design decision on their part. It would appear that they think this is not a good test to specify, because if you expect that a certain error not be raised, then the test will pass if:
no error is raised
some other error is raised
...which is at least, imprecise. Probably your code only wants to do one of these things.
That appears to be the reasoning, anyway -- I wouldn't like to say how fair it was.

From the changelog:
Make expect { }.to_not raise_error(SomeSpecificClass, message), expect { }.to_not raise_error(SomeSpecificClass) and expect { }.to_not raise_error(message) invalid, since they are prone to hiding failures.
Instead, use expect { }.to_not raise_error (with no args). (Sam
Phippen)

Seems like they realised that there was no reason to deprecate the exception class parameter, so it currently works: https://www.relishapp.com/rspec/rspec-expectations/v/3-5/docs/built-in-matchers/raise-error-matcher
For example, in Selenium, after checking for some condition to become true:
Selenium::WebDriver::Wait.new(
message: message,
timeout: timeout,
).until do
f() == x
end
it's now easy to also wait that it does not become false in next seconds by negating the timeout exception:
begin
Selenium::WebDriver::Wait.new(
timeout: timeout,
).until do
f() != x
end
raise message
rescue Selenium::WebDriver::Error::TimeOutError
end

Related

How to test a simple rescue block in ruby

I have a custom fact in ruby that goes like this:
Facter.add(:some_random_fact) do
setcode do
output = execute_some_method
if !output.nil? then
begin
pruned_output = output.split("\n")
result = true
rescue
result = false
end
else
result = false
end
end
end
How do I write a unit test using rspec for the rescue block to raise an Exception?
EDIT: Please let me know if the below test is the correct way to test it
it "return fact as false when begin block raises exception" do
output = double(:output)
allow(output).to receive(:split).with(true).and_raise(RuntimeError.new("error occured"))
expect(Facter.fact(:some_random_fact).vallue).to eq(false)
end
The code you've shown here is weird and I get the feeling we're missing context, but in general you can stub out a method to raise an error like so:
expect(output).to receive(:split).with("\n").and_raise(RuntimeError.new("some error"))
but this is sort of an ugly way to go about things. If the error is raised conditionally depending the type of output, then it's better to find a way to set that variable to an error-producing value. How to do that, I can't tell you without seeing the test of your code.
For example, say you wrapped all this code in a def add_fact(output) - then from your tests you could intentionally pass an error-causing value for the output, and you no longer need to stub split (which is a wierd thing to do). This pattern is known as "dependency injection".

Is there are a difference between change(receiver, message) and change { block }

I found that assertion method change used in two different ways
expect { createRecord.call }.to change(Record, :count).by(1)
vs
expect { createRecord.call }.to change { Record.count }.by(1)
I tried to dig into source code, and found that passed block will be called if block provided.
Without block message will be "sent" to the receiver.
I was wonder are there some scenarios where one should be preferred over another?
Not everything maps so neatly to the send approach. For example:
expect { createRecord.call }.to change { Record.count(OtherRecord.param) }.by(1)
Where there's no way to represent that as a simple send(*args) as:
expect { createRecord.call }.to change(Record, :count, OtherRecord.param).by(1)
This evaluates OtherRecord.param as the expect line executes, not at the right before and after interval.
It's provided for feature-completeness and to give you complete control.
In short the block form runs the exact block twice while the other evaluates the arguments once and makes a send call twice.

Catch a parameter error within a function?

Is it possible for a method to handle an error that happens in a parameter?
e.g.
def add(arg1, arg2)
# If the value causes an error, turn it into 0
rescue nil
0
end
arg1 + arg2
end
add(2, 2+nil) => 2
I understand that shielding parameters and eval-ing it later can be a solution, but in my scenario it becomes too cumbersome to do that.
The problem with this is the error occurs on the line where you provide the arguments as arguments are evaluated prior to the method being called.
Every method call is roughly equivalent to this:
arg1 = 2
arg2 = 2 + nil
add(arg1, arg2)
In this case you can see how the argument itself produces an error. The only way to defer the evaluation of an argument is via a block:
add(2) do
2 + nil
end
Changing your definition to:
def add(v)
v + yield
rescue
v + 0
end
Capturing all exceptions blindly is usually a super bad plan, you may have some kind of fundamental mistake in there. It's best to avoid capturing exceptions unless you have expectations as to what kind they will be.
Here's a version of the method with no exception handling:
def add(*args)
args.map(&:to_i).inject(:+)
end
Now nil.to_i returns 0 so you're covered:
add(2,2,nil)
That will evaluate to 2+2+0 internally.
Generally it's a bad plan to try and bury errors like 2+nil. That's a fundamental mistake that should be addressed. If you wanted to handle nil values you would use .to_i to map them down to something usable.
In this case your add method isn't still called and you have to rescue in the caller.

Is there an elegant way in Ruby which acts like `require` in Scala?

What I want to do is to make sure that arguments meet some conditions, if not, raise errors.
like this(let's say I want to make sure n > 0):
def some_method(n)
raise "some error" unless n > 0
... # other stuffs
end
There is require method in Scala which tests an expression, throwing an IllegalArgumentException if false.
if there is something acting like that in ruby?
I know ruby has assert series methods in unit test. But I don't think it is what I want.
EDITED
I just want to know if there are other ways to ensuring arguments meets some conditions, instead of raise.(The require in scala is so fit for that.)
What's wrong with your initial try? It works fine if you indeed want to throw Exceptions. You can create a method to test the requirement if you want, but it does not really do much:
def req(cond, error)
raise error if cond
end
def method(n)
req(n < 0, ArgumentError.new('YOU BROKE IT'))
# Method body
end
method(-1) # => method.rb:2:in 'req': YOU BROKE IT (ArgumentError)
If your problem is that you want to specify the error class, and want to write the condition to be satisfied rather than condition not to happen, then there is no special thing you need.
def some_method(n)
raise ArgumentError.new("some error") unless some_condition
raise ArgumentError.new("another error") unless another_condition
raise ArgumentError.new("yet another error") unless yet_another_condition
...
end

Waiting for an element without throwing an exception if timed out

I know of the method Element#wait_until_present(t), but if this method times out it throws a timeOut exception.
Is there a method that just waits for t seconds and then returns true if the element became present or false otherwise?
I know it can be done with a simple begin..rescue..end statement, but I'm looking for something that doesn't use exceptions.
You can write a short-hand rescue clause like this:
element_present = browser.element.wait_until_present rescue false
puts "element not present" unless element_present
This does however result in a false value on any Exception at all and not just with TimeoutError. I still prefer to use it since if there's any Exception at all then it would be safer to assume that the element was not present.
Looks like there is no other method that will do what i'm looking for ,
so here is the simplest method to achieve this :
#check method for Element#wait_until_present(t)
def check_if_present(element,t)
raise ArgumentError, 't must be a number ' unless t.is_a? Numeric
begin
element.wait_until_present(t)
true
rescue Watir::Wait::TimeoutError
false
rescue
raise "Something wrong with the element"
end
end
If you do not want an exception the below code can be handy:
sleep 'your time here' eg: sleep 20 - this will wait for 20 secs.
then check for your element now:
'your element'.exists? -this will return true/false
you will not get an exception this way.
Another best way is to write your wait_for_load method based on your needs.

Resources