0

I have a python module file (func.py) and a unit test file (f_test.py):

# func.py

def f(x):
    return x + 1

x = input("Enter x: "))

print("f(x): " + str(f(x)))

and

# f_test.py

import unittest
from func import f

class MyTest(unittest.TestCase):
    def test(self):
        self.assertEqual(f(1), 2)

When I run f_test.py I expect the test suite to be executed (successfully). Instead however I see the following input:

Finding files... done.
Importing test modules ... Enter x:

If I comment out the input/output lines from func.py then I get the expected behaviour. How do I achieve it without modifying func.py?

Inoryy
  • 8,365
  • 2
  • 39
  • 40
  • How is that not expected? You can't finish importing `func` until all the code finishes running, and the code can't finish running when it's waiting for you to answer the `input`. – abarnert Sep 17 '14 at 18:05
  • 3
    I know you said you don't want to modify func.py, but... You should modify func.py. Use a [`__name__` guard](http://stackoverflow.com/questions/419163/what-does-if-name-main-do) so `x = input` doesn't get executed when you import the module. – Kevin Sep 17 '14 at 18:09
  • Not just "should", pretty much the only way to avoid modifying func.py would be to install an import hook, or preprocess it and exec the result manually instead of importing it. (Well, I guess you could compile it, dig through it for the compiled code object, construct a function out of it, and run that… But that's not exactly simpler.) – abarnert Sep 17 '14 at 18:11
  • Also, it's worth noting that running `func.py` is guaranteed to raise a `TypeError: Can't convert 'int' object to str implicitly` no matter what you type, so… why don't you want to modify it in the first place? – abarnert Sep 17 '14 at 18:20
  • You can't unit test the functions in func.py because of the problems others have noted. You can execute func.py as a subprocess, pump some data in, and see what you get out. So, if you can't change func.py, you can change the test. – tdelaney Sep 17 '14 at 18:23
  • @tdelaney: Of course what he gets out will be nothing to stdout and a traceback to stderr, so I'm not sure how helpful that will be… but I guess if the point if to properly test his code and fail it for being broken, it will achieve that. – abarnert Sep 17 '14 at 19:32
  • 1
    @abarnert - I guess we can call it test-driven development. – tdelaney Sep 17 '14 at 20:06

3 Answers3

5

When you import func, all the code inside is run. Running the definition def f(x)... is what creates the function f.

You can distinguish between importing and running a file by using if __name__=='__main__'.

For instance:

# func.py

def f(x):
    return x + 1

if __name__ == '__main__':
    x = input("Enter x: "))
    print("f(x): " + str(f(x)))

When you run func.py, __name__=='__main__' will be true. When you import it, it will be false (__name__ will be 'func' instead).

khelwood
  • 55,782
  • 14
  • 81
  • 108
  • 2
    In case it isn't obvious to the OP: function (and class, etc.) definitions are code. Compiling the module doesn't create `f`, only running the code in it creates `f`. And Python can't see any difference between 'the code that creates `f`', which you want it to run, and 'the code that waits for input', which you don't; it's all just code. – abarnert Sep 17 '14 at 18:13
2

Of course, the correct answer is to modify func.py. If you absolutely, positively, won't modify func.py, then you could redirect standard input:

# f_test.py

import os
import sys
import unittest

oldstdin, sys.stdin = sys.stdin, StringIO.StringIO('7')
try:
  from func import f
finally:
  sys.stdin = oldstdin

class MyTest(unittest.TestCase):
    def test(self):
        self.assertEqual(f(1), 2)
Robᵩ
  • 163,533
  • 20
  • 239
  • 308
1

You need to add to the bottom of f_test.py:

if __name__ == '__main__':
    unittest.main()

This way the tests will be executed when the file is run (I forget this more times than I would like to admit).

clm42
  • 65
  • 1
  • 9