Rspec: Checking the content of a system call

Viewed 104

I have a Rake task in my Rails project which executes openssl through a system call.

The code looks like this:

system('bash', '-c', 'openssl cms -verify...')

I need to run the command in bash rather than dash (which is default on Ubuntu) to use process substitution in the command.

I need to create a test with rspec which checks that, in this case, the argument verify was passed as expected.

I have tried the following:

expect(Kernel).to receive(:system) do |args|
  expect(args[2]).to match(/verify/)
end

However, this only gives me the third letter in the first string sent to system - i.e. the letter s from bash - rather than the third argument sent in the system call.

What am I doing wrong here? Any suggestions would be much appreciated.

1 Answers

Args are being passed to the block as sequential arguments, so if you want to treat them as an array, you need a splat operator in do |*args|:

expect(Kernel).to receive(:system) do |*args|
  expect(args[2]).to match(/verify/)
end 

Just to take a step back, it's important to understand how block arguments work, since they are different from methods. For example:

def my_fn(*args)
  yield(*args)
end

my_fn(1,2,3) { |args| print args }
# => 1

my_fn(1,2,3) { |a, b, c| print [a,b,c] }
# => [1,2,3]

my_fn(1,2,3) { |*args| print args }
# => [1,2,3]

So if you did do |args| (without the splat), you are assigning the args variable to the first argument passed to the block ("bash") and ignoring the other arguments.

Related