7

I'm using nose to test an application that uses subprocess.Popen() to call a script. Neither the capture or logcapture plugin seem to capture the output of this script. Is there an easy way to pipe this output to nose?

Here's what I've attempted so far; note "Capture me" isn't captured:

example.py:

if __name__ == '__main__':
    print 'Capture me'

test.py:

import subprocess
import sys


def test_popen():
    # nose's capture plugin replaces sys.stdout with a StringIO instance.
    # subprocess.Popen() expects stdout to have a fileno property, which
    # StringIO doesn't, so fake it.
    sys.stdout.fileno = lambda: 1

    subprocess.Popen(['python', 'example.py'], stdout=sys.stdout)
    assert False # Force test to fail so we see nose output

output:

$ nosetests test.py
F
======================================================================
FAIL: test.test_popen
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/Users/pmdarrow/Envs/example/lib/python2.7/site-packages/nose/case.py", line 197, in runTest
    self.test(*self.arg)
  File "/Users/pmdarrow/Code/example/API/test.py", line 8, in test_popen
    assert False
AssertionError

----------------------------------------------------------------------
Ran 1 test in 0.004s

FAILED (failures=1)
Capture me
pmdarrow
  • 734
  • 5
  • 14

2 Answers2

3

There are several issues that you have to be aware. I might be repeating @arbunet answer, but bear with me a little here. So if you wait for the process to finish, and redirect process' stdout to the test's stdout, everything will be captured properly with nose:

import subprocess
import sys

def test_popen():
    p = subprocess.Popen(['python', 'example.py'], stdout=subprocess.PIPE)
    out, err = p.communicate()
    print out

    assert False

Here the PIPE is created and redirected into stdout using print statement. Alternatively, you could pass in sys.__stdout__ (unmolested stdout of the test) or other file descriptor to the Popen call and later flush it into sys.stdout (StringIO.StringIO instance created by nose), but it looks less clean (compared to a simple print statement).

Jonathan Hall
  • 75,165
  • 16
  • 143
  • 189
Oleksiy
  • 6,337
  • 5
  • 41
  • 58
  • Using communicate won't quite work. You need to spawn a thread that *reads* from the p.stdout and writes to *sys.stdout*. Otherwise the captured logs will all be associated with the "last test" and not show. – Erik Aronesty Nov 15 '17 at 16:40
0

You might consider:

p = subprocess.Popen(['python', 'example.py'], stdout=subprocess.PIPE)
q = p.communicate()
output = q[0]

See communicate for more info from subprocess. Cheers!

abrunet
  • 1,122
  • 17
  • 31
  • I'm not sure this solves my problem. I'm not asking how to capture the output from a script, I'm asking how to get nose to capture this output and display it nicely in test results. – pmdarrow May 15 '14 at 13:55
  • getting it with communicate will enable you to use it in your test. Then you can do whatever you want with nose. I'm sorry I can't help more, I'm no expert with nose... :( But it should enable you to have your test to pass! Cheers! – abrunet May 15 '14 at 13:58
  • Thanks for the answer but I'm looking for a nose-specific solution. – pmdarrow May 15 '14 at 13:59
  • You can then print the piped output, and nose will capture it – Erik Aronesty Nov 15 '17 at 16:34