[TIP] Unittesting command line scripts with unittest2?

Jorge Vargas jorge.vargas at gmail.com
Wed Jul 28 15:10:12 PDT 2010

On Wed, Jul 28, 2010 at 5:38 PM, Michael Foord
<fuzzyman at voidspace.org.uk> wrote:
> On 28/07/2010 21:59, Jorge Vargas wrote:
>> I started writing some tests using this skeleton.
>> http://paste.ofcode.org/wr4JtC36nsMEWUVYPdSqj2
>> That is doing two things
>> 1- silencing the print statements as well as the subprocess calls
>> 2- giving me a variable to test the output.
> I'm afraid I don't understand what you are wanting to do. Is main() in that
> code unittest2.main() ?
Sorry about that. I forgot the context. in this code main is my main function.

It basically does some optparse on it's own so my thinking was to set
sys.argv for something it will expect. and then calling my code.

> I don't think that replacing sys.stdout will silence the output of
> subprocesses. The subprocess module itself has ways of collecting output
> though.
it does if done in a clever way. One of the things I'm doing is
unifying all our calls into this function:

def system(cmds):
   subprocess.Popen(cmds, stdout=subprocess.PIPE, stderr=subprocess.PIPE)

This way all the output gets into the same stdout/stderr variables.

>> So I have two questions, is this a good approach? is there a better
>> way? perhaps it should be abstracted into a separate module, maybe a
>> unittest2 plugin?
>> Also Today I realized there is something wrong with this approach. I'm
>> messing around with sys.argv which when called as `unit2 discover`
>> gives some troubles. In fact after upgrading to the plugins branch of
>> unittest2 I realized my tests are broken as sys.argv[1] will give an
>> error. I assume this is a side effect for getopt which was removed in
>> http://hg.python.org/unittest2/rev/0190b218cf49 therefore this is a
>> minor incompatibility between unittest2-0.5.1 and tip.
> sys.argv will contain whatever the *original* test runner script was called
> with. None of the unittest2 code modifies sys.argv and I don't think that
> has changed since I stopped using getopt. If your tests depend on the
> contents of sys.argv you can set or modify the list. (At least I'm pretty
> sure I don't mutate sys.argv - I would consider that bad practise, feel free
> to point it out if I am but a cursory glance doesn't show anything.)

Right, I just tested this and sys.argv defaults to /path/to/unit2 and
the args I passed in, silly me ;)

> There is a difference in the plugins branch: calling unit2 on its own (no
> args) will launch test discovery (equivalent of `unit2 discover`), in this
> case there will be no sys.argv[1]. Could this be what has happened?
I believe this is the problem. As the error I'm getting is IndexError,
since sys.argv is shared then as a side effect my original code worked
and now it stopped.

Which brings me back to the original question, is this a good way of
testing a cli script? or should I mock away the optparse part in some

Perhaps I just need to replace sys.argv entirely ie: set it as
sys.argv = ['/mock/path/to/my/script/','status']
main() # my main function

More information about the testing-in-python mailing list