RSpec: how do I write a test that expects certain output but doesn't care about the method?
take a look at this post. Nick raised questions about the same example, and a very interesting conversation follows in the comments. Hope you find it helpful.
Ruby and RSpec - Test fails when expected output is the same as the method
The correct way to write this test is to be verbose about your expectation. Test the exact value of what you expect ii to give. p
will output a new line so write this way.
RSpec.describe Board do
let (:new_board) {Board.new}
it 'prints board' do
p_output = "[[nil, nil, nil, nil, nil, nil], [nil, nil, nil, nil, nil, nil], [nil, nil, nil, nil, nil, nil], [nil, nil, nil, nil, nil, nil], [nil, nil, nil, nil, nil, nil], [nil, nil, nil, nil, nil, nil]]\n"
expect{new_board.make_and_print_board}.to output(p_output).to_stdout
end
end
But you might want to add this spec if you care more about the internals:
it 'it outputs a 6 x 6 2d array' do
expect( new_board.make_and_print_board ).to match_array Array.new(6) { Array.new(6)}
end
How to test for stdout that is inside a .each iteration block, using rspec?
Todd's answer is fine and I'd strongly recommend you read it carefully: refactor your app in a way that UI (CLI in your case) is minimal and easy to test. But when you want full coverage you'd need to test that std output eventually.
The way you're using it now:
expect(initialize_program(1, 3)).to output("whatever").to_stdout
Means that initialize_program(1, 3)
is evaluated immediately when the matcher is called, and it's too soon - it has to do it's magic(*) first, and then run your code. Try like this:
expect { initialize_program(1, 3) }.to output("whatever").to_stdout
Now, instead passing results of calling initialize_program(1, 3)
into the matcher, you pass a block that "knows how" to call initialize_program(1, 3)
. So what the matcher does:
- saves the block for later
- does it magic to capture whatever goes to the STDOUT (see below)
- calls the block, calling the
initialize_program(1, 3)
, capturing whatever was supposed to go to STDOUT - compares it with what you've set up in your expectation (the
output("whatever")
part)
https://relishapp.com/rspec/rspec-expectations/docs/built-in-matchers/output-matcher
- The mentioned magic is not that magical anyway:
https://github.com/rspec/rspec-expectations/blob/44d90f46a2654ffeab3ba65eff243039232802ce/lib/rspec/matchers/built_in/output.rb#L49
and
https://github.com/rspec/rspec-expectations/blob/44d90f46a2654ffeab3ba65eff243039232802ce/lib/rspec/matchers/built_in/output.rb#L141
It just creates StringIO, and replaces global var $stdout with it.
RSpec test looped output
When you write with(a, b)
, you're telling RSpec you expect the method to be called once with arguments a, b
. For example, this will pass:
it 'prints' do
expect(STDOUT).to receive(:puts).with(1, 2)
puts 1, 2
end
You're trying to do something different; you want puts
to be called with book_a
, and then called again with book_b
. You can use ordered
to specify this to RSpec.
it 'prints each book' do
expect(STDOUT).to receive(:puts).with('1. Harry Potter').ordered
expect(STDOUT).to receive(:puts).with('8. Lord of the Rings').ordered
@library.print_books
end
RSpec will now check that puts
is called first with "1. Harry Potter"
, then with "8. Lord of the Rings"
. If one is missing, a third book is present, or the calls are made in the wrong order, the test will fail.
RSpec descriptions: Why does the `should` come before the method that should be causing it?
From the official doc: https://www.relishapp.com/rspec/rspec-mocks/v/2-5/docs/message-expectations/expect-a-message
"Use should_receive() to set an expectation that a receiver should receive a message before the example is completed."
Read the two bold words above you may have a better understanding of this method. When you set should_receive()
, it established an expectation and will watch the code run below in this example(it block)
So this method only makes sense if you set it before and run the code later. This should be able to explain your question.
Rspec -- How to test method by calling it and checking for change in object
The problem is that in the context in which you call it, self
refers to an object of class RSpec::Core::ExampleGroup::Nested_1
, and since you don't provide an explicit receiver, it's that self
object that receives the message.
As @pjam notes, without more information it's impossible to explain how exactly to fix your test. But the general point is that the update_user_region_id
message needs an appropriate receiver. If, for example, that were a class method on the Foo
class, you could call Foo.update_user_region_id
. Or if it's an instance method, you may need to instantiate an object first (e.g., Foo.new.update_user_region_id
).
Basic RSpec issue - method runs but tests fail
Add cipher
or return cipher
after this line
puts "The encrypted string is: #{cipher}"
And it should work
To explain the fix given, the last expression in a method is the return value. You've passed the value to STDOUT but not as the return value of the method, so RSpec was failing.
Related Topics
Does Rails Come with a "Not Authorized" Exception
How to Determine Leap Year in Ruby
How to Create a Form in Rails Without Having to Use Form_For and a Model Instance
Initializing Instance Variables in Mixins
How to Globally Ignore Invalid Byte Sequences in Utf-8 Strings
Running Webrick Server in Background
Ruby: How to Send a JSON Post Request Using Curb
How to Sort a Ruby Hash Alphabetically by Keys
"Use" Keyword/Word in Ruby/Rails/Rack Code
Passing Binding or Arguments to Erb from the Command Line
How to Print Information About a Net:Httprequest for Debug Purposes