2

I am developing some test cases in Ruby using rspec.

I am attempting to mock the popen3 function.

However, while still keeping the blocking form, I am unable to capture the expected output information:

Class MyClass
  def execute_command
    Open3.popen3(command) do |stdin, stdout, stderr, wait_thr|
      output['wait_thr'] = wait_thr.value
      while line = stderr.gets
         output['stderr'] += line
      end
    end
    return output
  end  
end

To mock out the function, I am doing the following:

it 'should do something'
  response = []
  response << 'stdin'
  response << 'stdout'
  response << 'test'
  response << 'exit 0'

  # expect
  allow(Open3).to receive(:popen3).with(command).and_yield(response)

  # when
  output = myClassInstance.execute_script

  #then
  expect(output['wait_thr'].to_s).to include('exit 0')

Mocking out the function doesn't enter the "do" code and I'm left with an empty data structure.

I was wondering how I could properly do this?

Thanks!

Parth
  • 1,226
  • 7
  • 28
  • 49
  • Could you provide the full spec? – wicz Sep 26 '14 at 09:00
  • 1
    And this spec isn't returning any error? It's a bit confusing. You're mocking the `response` as an array, but `expect` it if it was a hash. And the code itself, you're defining `output` inside the block, and returning in the end of the method. It should return and error, because the variable is undefined out the block. Or maybe your now sharing the whole code. This makes harder to help. – wicz Sep 26 '14 at 20:13
  • Sorry, I forgot to include it. `Open3 yielded || to block with arity of 4` is the error that I receive. Anything between the pipes is the set of arguments passed to and_yield – Parth Sep 26 '14 at 21:48
  • I ended up replacing `popen3` with `capture3` as for what I'm doing, it works. Not really a solution, but a work around – Parth Sep 30 '14 at 17:41

2 Answers2

3

To add some more context to Chris Reisor's answer, this is the approach that worked for me:

I have a piece of code that reads as shown here.

Open3.popen2e(*cmd) do |_, stdout_and_stderr, wait_thr|
  while (line = stdout_and_stderr.gets)
    puts line
  end

  raise NonZeroExitCode, "Exited with exit code #{wait_thr.value.exitcode}" unless wait_thr.value.success?
end

And my testing setup looks like shown below.

let(:wait_thr) { double }
let(:wait_thr_value) { double }
let(:stdout_and_stderr) { double }

before do
  allow(wait_thr).to receive(:value).and_return(wait_thr_value)
  allow(wait_thr_value).to receive(:exitcode).and_return(0)
  allow(wait_thr_value).to receive(:success?).and_return(true)
  allow(stdout_and_stderr).to receive(:gets).and_return('output', nil)
  allow(Open3).to receive(:popen2e).and_yield(nil, stdout_and_stderr, wait_thr)
end
Reck
  • 7,966
  • 2
  • 20
  • 24
2

I think you needed to put "*response" instead of "response."

allow(Open3).to receive(:popen3).with(command).and_yield(*response)

That will send 4 string args to and_yield ("arity of 4"), rather than one arg which is an array.

Chris Reisor
  • 111
  • 1
  • 3