Rspec: How to Write a Test That Expects Certain Output But Doesn't Care About the Method

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:

  1. saves the block for later
  2. does it magic to capture whatever goes to the STDOUT (see below)
  3. calls the block, calling the initialize_program(1, 3), capturing whatever was supposed to go to STDOUT
  4. 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



Leave a reply



Submit