<div dir="ltr">I added a couple new helpers to the testing repo that I think will help us avoid some of the contortions we have had to do in the past when testing executables and output on stderr/stdout.<div><br></div><div>The first is a fairly simple function, <a href="https://godoc.org/github.com/juju/testing#CaptureOutput">CaptureOutput</a>, which takes in a func() and returns anything written to stderr or stdout while running that function.  It temporarily replaces os.Stderr and os.Stdout with files on disk while running the function (necessary because those two values are *os.File values, so, rather hard to mock out with anything in-memory).</div><div><br></div><div>The second is slightly more complicated - <a href="https://godoc.org/github.com/juju/testing#PatchExecHelper">PatchExecHelper</a> </div><div><br></div><div>We had PatchExecutable, which creates gnarly bash/batch scripts on disk to replace expected executables, but it has a few weaknesses - it doesn't allow you to test the arguments passed to it *and* test stderr/stdout at the same time, it assumed the thing you were patching out was on the PATH, and I'm pretty sure it wouldn't work to patch out .exe files on Windows.</div><div><br></div><div>PatchExecHelper fixes all of those problems by approaching the problem in a different way, it creates a function that is intended to mock out a call to exec.Command.  It's very easy to use, just embed PatchExecHelper in your test suite, and then use its GetExecCommand() method to get a function that can be used to mock out exec.Command.</div><div><br></div><div>The exact method of how it works is detailed in the godoc, and it's an example implementation of something I blogged about a while back: <a href="https://npf.io/2015/06/testing-exec-command/">https://npf.io/2015/06/testing-exec-command/</a></div><div><br></div><div>Here's an example of how it can be used:</div><div><br></div><div>// production code</div><div>var execCommand = exec.Command</div><div>func Run(command string, args ...string) ([]byte, error) {</div><div>    return execCommand(command, args...).CombinedOutput()</div><div>}</div><div><br></div><div>// test code</div><div>type RunSuite struct {</div><div>    testing.CleanupSuite</div><div><br></div><div>    // you *must* embed PatchExecHelper in your test suite.</div><div>    testing.PatchExecHelper</div><div>}</div><div><br></div><div>func (s RunSuite) TestRun(c *gc.C) {</div><div>    outArgs := make(chan []string, 1)</div><div>    f := s.GetExecCommand(testing.PatchExecConfig{</div><div>        Stderr: "this is stderr!",</div><div>        Stdout: "this is stdout!",</div><div>        ExitCode: 5,</div><div>        Args: outArgs,</div><div>    })</div><div>    s.PatchValue(&execCommand, f)</div><div><br></div><div>    output, err := Run("echo", "hello world!")</div><div>    args := <-outArgs</div><div>    // test output, args, etc.</div><div><span style="line-height:1.5">}</span><br></div><div><br></div><div>Let me know if you have any questions or comments.</div><div>-Nate</div></div>