tomdee
tomdee

Reputation: 2439

How can a piece of python code tell if it's running under unittest

I have a large project that is unit tested using the Python unittest module.

I have one small method that controls large aspects of the system's behaviour. I need this method to return a fixed result when running under the UTs to give consistent test runs, but it would be expensive for me to mock this out for every single UT.

Is there a way that I can make this single method, unittest aware, so that it can modify its behaviour when running under the unittest?

Upvotes: 46

Views: 19831

Answers (7)

Mr. Girgitt
Mr. Girgitt

Reputation: 2903

Checking for 'unittest' module in sys.modules can result in a false-positive result in case of certain modules used in the code like numpy which loads unittest module.

Depending on the testing framework used, it could be a better idea to look for module from the testing framework. In my case I check for 'nose' module instead of 'unittest' as I execute tests with nosetests:

if 'nose' in sys.modules:
    print("during UT - skipping expensive initialization")
else:
    print("full initialization")

Upvotes: 0

kwarnke
kwarnke

Reputation: 1514

You can check, if the unittest module is loaded. It should be loaded only, if a test runs.

>>> 'unittest' in sys.modules.keys()
False
>>> from unittest import TestCase
>>> 'unittest' in sys.modules.keys()
True

Upvotes: 29

Agost Biro
Agost Biro

Reputation: 2839

With tox I set an environment variable like this:

[testenv]
setenv = TOX_TESTENV = true

and then in the code I check if the variable is set:

import os


if os.env.get('TOX_TESTENV'):
    # Code is running under test.
    # This is useful to configure log levels for example.

Upvotes: 3

TheSoundDefense
TheSoundDefense

Reputation: 6935

I don't know much about the unittest module, but if you're running the file directly for the unit test, you can enclose the test code with the following if:

if __name__ == "__main__":

Any code that lies within that if statement will only be executed if your particular module is being directly invoked, and not imported into something else. According to the docs, that's how you should be calling unittest.main() in the first place.

https://docs.python.org/2/library/unittest.html

This assumes you're not running from the command line.

EDIT: you could look at the function stack to try and find the unittest.main() function.

import inspect

def in_unit_test():
  current_stack = inspect.stack()
  for stack_frame in current_stack:
    for program_line in stack_frame[4]:    # This element of the stack frame contains 
      if "unittest" in program_line:       # some contextual program lines
        return True
  return False

https://docs.python.org/2/library/inspect.html

It's kind of a hacky solution, but the inspect module has a lot of useful functions for introspection.

Upvotes: 6

Franco
Franco

Reputation: 717

My solution is to set a TEST_FLAG=true environment variable before running unittest. For example:

TEST_FLAG=true python -m unittest discover -s tests -b

Then it is just a matter of checking if the variable is set. For example:

MONGODB_URI =
    os.environ.get('MONGODB_URI') if not os.environ.get('TEST_FLAG') 
        else os.environ.get('MONGODB_TEST_URI')

Upvotes: 13

Greg Hewgill
Greg Hewgill

Reputation: 992747

You can modify a function at runtime just for the tests. For example:

module.py

def func():
    return random.randint()

test.py

import module

def replacement_func():
    return 4 # chosen by fair dice roll

module.func = replacement_func

# run unit tests here

Now, whenever code in module calls func(), then it will actually call back out to your replacement_func().

Upvotes: 1

Steve Barnes
Steve Barnes

Reputation: 28370

I am sure that there are other, better, methods but you could always set a global flag from your main and not under unit test then access it in your method.

The other way of course would be to override the method as a part of the unit test set-up - if your method is called brian and you have a test_brian then simply during your pre-test setting brian = test_brian will do the job, you may need to put module names into the preceding statement.

Upvotes: 1

Related Questions