Update: I have refined this technique and uploaded a more full-stack example to GitHub. Testing Perl code that runs commands.
A lot of the code I end up needing to work on makes calls to various executables to read and process system state. It might be DTrace, or one of the tools that comes with the Virtuozzo/OpenVZ management suite, or one of the HP hardware monitoring tools – they all want to be called from the command line.
In the old days, I probably would have just used perl backticks to run the command and capture the output. I’d “test” on a machine that had those tools installed, and call it a day.
1 2 | |
However, that’s pretty evil, and it’s essentially untestable.
Here’s a pattern that works pretty well.
The IPC::Run module includes a ‘run’ command, which in the simple form takes 4 references: To a list of a command and it’s arguments, and then scalar references for the contents of Standard In, Standard Out, and Standard Error.
If all you care about is capturing STDOUT, you might call it like:
1 2 3 | |
Ok, but how does that help us test? Enter the venerable Test::MockModule.
All I need to do is override the run() function for the module in my test!
1 2 3 4 | |
Ok, so how do I get the right content to get in the $raid_output?
It depends on how exactly you’re calling run: I hand-tuned this for the specific use case I’m doing right now, where I just care about capturing output.
What we do is store a list of command lines in the data block, followed by their output. When someone calls ‘run()’ with one of those command lines, we send that output back to them.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | |
And the matching section in the DATA block:
1 2 3 4 5 6 7 8 9 10 11 12 | |
And you’re done! This is nice and easily extensible. If you have several specific use cases (like output that actually starts with ‘* ‘, for example, or needing STDIN/STDERR) you’d need to modify it. It’s a handy general pattern for making “sysadmin code” a lot more like “developer code”, though. (The great thing is I was able to write and test this whole code suite on a VM that didn’t even have an HP raid controller, let alone hpacucli installed, for example.)