What would be the best way to write the rspec in a situation where either of two (or more) outcomes are acceptable?
Here's an example of what I want to do. This is obviously wrong (I think), but it should give you the gist of what I'm trying to accomplish:
it "should be heads or tails" do
h="heads"
t="tails"
flip_coin.should be(h || t)
end
And yes, I'm aware I could write my own rspec matcher "should_be_one_or_the_other(option1,option2)", but that seems a bit much - I was hoping for a better solution.
ActiveSupport provides Object#in? method. You can combine it with RSpec and simply use the following:
flip_coin.should be_in(["heads", "tails"])
Or with new Rspec 3 syntax:
expect(flip_coin).to be_in(["heads", "tails"])
I know this is old but in I ran into this on RSpec 3.4, there is an or method now. So this is valid:
expect(flip_coin).to eq('heads').or(eq('tails'))
I'd probably write something like this:
it "should be heads or tails" do
["heads", "tails"].should include flip_coin
end
Another way of writing it with the expectation on the right of the should:
it 'should be heads or tails' do
flip_coin.should satisfy{|s| ['heads', 'tails'].include?(s)}
end
if applied or with be matcher
expect(flip_coin).to eq('heads').or(be == 'tails')
You can solve this by flipping the comparison:
expect(['head','tails']).to include(flip_coin)
Related
I have a function I'm calling that I need to test the arguments of. Normally I'd do something like:
expect(my_obj).to_receive(:my_function).with(include('good_value'))
Is there a way to flip this around, though, to be something like
expect(my_obj).to_receive(:my_function).with(exclude('bad_value'))
In a spec on a return value this is simply accomplished using to_not, but that form of negation is not available when matching arguments it seems. I can find nothing in the docs suggesting a way to do this other than writing a custom matcher, which feels like overkill here.
Is there a simple way I can do this without having to write a custom matcher?
RSpec 3.1+ allows you to define a negated version of any matcher using RSpec::Matchers.define_negated_matcher:
RSpec::Matchers.define_negated_matcher :exclude, :include
Once you've done that, this should work:
expect(my_obj).to_receive(:my_function).with(exclude('bad_value'))
You may try:
http://www.rubydoc.info/gems/rspec-mocks/RSpec/Mocks/ArgumentMatchers#hash_excluding-instance_method
expect(object).to receive(:message).with(hash_excluding(:key => val))
Is there a more elegant way of writing this?
#on_connection_callback.call() if #on_connection_callback
It's having to reference #on_connection_callback twice that's irking me.
you may write this as
#on_connection_callback.call() rescue nil
I like things like the "andand" gem which allows:
#on_connection_callback.andand.call()
There are other options, like various try implementations.
#on_connection_callback.call() if #on_connection_callback
is fine, and is actually the fastest way to test. I recently did a benchmark test here proving that the above is slightly faster than:
#on_connection_callback && #on_connection_callback.call()
Use this little gem:
tryit { #on_connection_callback.call() }
#on_connection_callback.instance_eval{call if self}
Or
->p{p.call if p}.call(#on_connection_callback)
I think I would see my code better if I would ask myself object.not_nil? vs !object.nil?. So my question: Is there really no convenience method for !nil? to sugar things up? Is it in front of my eyes and I cannot see it or am I just missing an important point?
How about this?
not object.nil?
But the easier thing to do would be to check for the "truthiness" of by testing the variable itself. Since nil is implicitly false you can just check object.
You can introduce the sugar at an upper level. Instead of:
if not object.nil?
you can write:
unless object.nil?
What about this ?
if object
# sth
end
It is not the same as it will not be executed if object is false but depending on you code, it could be better.
Another solution (which is not the same either), as you tagged your question with ruby-on-rails-3 : using present? which will not execute the block for [] or {} unlike !object.nil?.
Again another one depending of the case : using unless which won't be really nice if your condition is more complex (with && and/or ||).
If your condition is of this form :
if !object.nil? && object.something?
# sth
end
You can use try, as you are using Rails, like this :
if object.try(:something?)
# sth
end
In all the other cases, !object.nil? or not object.nil? stays the best solution I guess.
When convenience around #nil? is discussed, Activesupport's methods #blank? and #present? shouldn't be forgotten either.
Not that you'd necessarily want to, but you can introduce not_nil? yourself:
class Object
def not_nil?
!self.nil?
end
end
then you can do things like:
nil.not_nil?
==> false
3.not_nil?
==> true
a = []
a.not_nil?
==> true
Not sure if this is possible but can I call a method from an irb shell with spaces between parameters rather than commas (don't ask) ? Lets say I have a method
def start_band(member1, member2, member3, member4)
#do something
end
And then I call it like the following:
irb>> start_band "John" "Paul" "George" "Ringo"
EDIT: Would it be possible to detect every keypress instead?
No, you can't do that. Not with strings anyway.
No.
You could use something like treetop to write a really simple DSL, or just play monkey-parsing games, but that won't solve your exact question.
The other obvious answer is this, which also fails:
irb>> start_band %W(John Paul George Ringo)
Creating an irb-like CLI isn't difficult, and may be adequate, depending on what your actual requirements are.
There is actually a very easy way to get rid of the commas. You can even get rid of the quotes, too:
def start_band(members)
#members is an array
end
start_band %w(John Paul George Ringo)
The limitation is that you can't use spaces inside your strings, and you still need start-end terminations (can use other characters instead of parenthesis though).
Durr! I really approached this the wrong way. I simply needed to run
#members = gets
to allow the input as required. Thanks for the responses nonetheless.
I'm wanting to use the &method(:method_name) idiom when there's more than one object required by method_name. Can I do this under Ruby 1.9?
For example, if I've got
def move_file(old_filename, new_filename)
STDERR.puts "Moving #{old_filename.inspect} to #{new_filename.inspect}"
# Implementation for careful moving goes here
end
old_filenames = ["foo.txt", "bar.txt", "hoge.ja.txt"]
new_filenames = ["foo_20110915.txt", "bar_20110915.txt", "hoge_20110915.ja.txt"]
the code
old_filenames.zip(new_filenames).each(&method(:move_file))
works under Ruby 1.8, but not under Ruby 1.9. Under Ruby 1.9, it's trying to do move_file(["foo.txt", "foo_20110915.txt"]) instead of move_file("foo.txt", "foo_20110915.txt").
How do I splattify it so it has the correct arity?
Workarounds I'm aware of:
Replace def move_file(old_filename, new_filename) with def move_file(*arguments)
Replace each(&method(:move_file)) with
each{|old_filename, new_filename| move_file(old_filename, new_filename)}
Instead
each{|old_filename, new_filename| move_file(old_filename, new_filename)}
you should be able to do
each{|pair| move_file(*pair)}
But I don't know how you'd pull off blockless variant (I needed it couple of times as well). I guess &-shorthand was made to make the syntax simpler, and is not meant to be clogged much (whether it will be passed an array as an array, or splatted, for example). :)
How do I splattify it so it has the correct arity?
I don't think there is a way to do this while being compatible to both Ruby versions. What you could do is wrap it into a lambda
move_from_to = Proc.new {|*both| move_files(*both) }
The thing is - block and proc arity is something that got addressed in Ruby 1.9 so there might be a difference in behavior there. Also see prc.lambda? here http://www.ruby-doc.org/core/classes/Proc.html for info on what it does to the arity.
This question is also related to what you want to do (the solution there is to resplat and unsplat manually): Inconsistency of arity between Hash.each and lambdas