Unit testing is part of software development to ensure the tiny component of a function can work as designed. Different frameworks and tools can be used to run unit testing for different programming languages. In Ruby, one popular unit testing framework is Rspec, or Chefspec if you are writing Chef recipes.
While writing Chef recipes, some low level commands(DOS commands or shell commands) need to be executed on the managed resource to perform actions or gather information. For example, listing directory and files on an Unix system using ls command. Mixlib-Shellout can be used to execute on a managed resource programmaticlly so that a server can be managed automatically using programs.
For example, to find .rb files in current directory, below Ruby code can be written
require 'mixlib/shellout' find = Mixlib::ShellOut.new("find . -name '*.rb'") find.run_command
The first line is to require the library and the second line creates a Mixlib::ShellOut instance and the third line executes the actual find command. The command will return an object which contains the stdout and stderr of the command.
Below code can also be written to execute command by using shell_out.
require 'mixlib/shellout' include Chef::Mixin::ShellOut find = shell_out("find . -name '*.rb'")
shell_out is provided by Chef::Mixin::ShellOut and will execute the command and return the result of the execution including exitstatus etc.
Since this involves running command on a testing machine, the execution may be out of control and it may require physical connection to the machine as well, it is desired to stub the command while running unit testing which should not depend on external environment. To stub Mixlib::ShellOut, below code can be written.
output = "DUMMY OUTPUT" let(:shellout) { double(run_command: nil, error!: nil, stdout: output, stderr: double(empty?: true), exitstatus: 0, live_stream: nil) } before { Mixlib::ShellOut.stub(:new).and_return(shellout) } context "On testing something" do executor = SomeExecutor.new it "should return normally" do allow(shellout).to receive(:live_stream=).and_return(nil) expect(executor.execute("ls")).to eq(output) end end
Basically in the testcase, a Double is created which can simulate how Mixlib::ShellOut works, then this Double object will be returned when the Mixlib::Shellout.new is invoked every time when a testcase is ran.
The SomeExecutor.execute method may contain code creating Mixlib::Shellout and run command, in this case, the stdout from the Double object will be returned which is "DUMMY OUTPUT" in this example.