Hello everyone,
I’ve been tinkering with a program that uses Process.run
, etc in a somewhat crucial capacity. In Ruby, I can use RSpec to expect certain arguments and inject return values in a terse manner, but obviously in Crystal something more appropriate for its static nature is required.
So I decided I’d implement an alternative class called Cmd
to be used in my program, mostly so I could safely re-open it in spec_helper
:
class Cmd < Process
@@ran = [] of Tuple(String, Array(String))
@@yield_process : Nil | SpecProcess
def self.ran
@@ran
end
def self.run(**args : Process.run)
@@ran << args
end
def self.spec_yield_process(value)
@@yield_process = value
yield
@@yield_process = nil
end
def self.run(*args)
@@ran << args
yield @yield_process
end
def self.spec_reset
@@ran.clear
end
end
It works okay:
describe Grub, "#run_mkconfig" do
before_each do
Cmd.spec_reset
end
it "runs it on a path" do
Grub.new.run_mkconfig("/test/path")
Cmd.ran.should eq([{"/usr/sbin/grub2-mkconfig", ["-o", "/test/path"]}])
end
end
Now I want to write something to allow me to assert something about the args that go into Cmd
and inject its stderr. Something like this:
Cmd.run("ls", ["/doesnotexist"], error = Process::Redirect::Pipe) do |p|
p.error.gets_to_end
end
But trying to compile specs against this yields something like this:
In spec/spec_helper.cr:29:11
29 | @@ran << args
^-
Error: no overload matches 'Array(Tuple(String, Array(String)))#<<' with type Tuple(String, Array(String), Process::Redirect)
OK, sensible enough. I suppose I could copy in Process.run
's arguments into a NamedTuple and that’d be fine, but I thought to pause here and ask:
- Is there a better way to refer to the tuple/named tuple of a method’s parameters than copying it?
- Do you have any ideas for performing this kind of injection & assertion in general?
- If I copy the entire
Process.run
parameter-struct, is it going to make my assertions unwieldy?
Thank you for your considerations.