112

I'd like to connect to a different database if my code is running under py.test. Is there a function to call or an environment variable that I can test that will tell me if I'm running under a py.test session? What's the best way to handle this?

Laizer
  • 5,932
  • 7
  • 46
  • 73
  • Need more details. py.test, database? These are somewhat generic. – user590028 Aug 07 '14 at 17:11
  • 1
    py.test is the testing system (pytest.org). My DB here in Mongo, but it could be any DB, methinks. – Laizer Aug 07 '14 at 17:21
  • Ahh.. Sorry I couldn't be more helpful. Glad you found the answer. – user590028 Aug 07 '14 at 17:23
  • 2
    This question is getting negative votes because it's considered "bad practice". For me, I have a lot of experiments where I just want to test if they run without error. Running things to completion takes way too long, so I've been passing around a "test_mode" argument to every experiment I want to test this way, which just does various things to shorten the experiment while still running through all the code. This approach is very repetitive, and kind of sloppy, I'd prefer just to ask the system if I'm in a test. Does anybody have a nicer solution than the one accepted below? – Peter Apr 13 '15 at 17:37

6 Answers6

125

A simpler solution I came to:

import sys

if "pytest" in sys.modules:
    ...

Pytest runner will always load the pytest module, making it available in sys.modules.

Of course, this solution only works if the code you're trying to test does not use pytest itself.

ramnes
  • 1,880
  • 2
  • 11
  • 14
  • 8
    Seems like a cleaner / more pythonic solution if you ask me – NirIzr Jul 26 '18 at 00:25
  • 2
    Much nicer answer that could be used for other test frameworks like `nose` as well – RichVel Oct 17 '18 at 08:18
  • 3
    Likewise, this is the [recommended approach for detecting py.test under Django](https://github.com/pytest-dev/pytest-django/issues/333#issuecomment-302429237). – Cecil Curry Jan 15 '19 at 23:32
  • 1
    Used as a workaround for [pytest Issue #4843](https://github.com/pytest-dev/pytest/issues/4843) Thanks! – JamesThomasMoon Feb 27 '19 at 07:21
  • 3
    Warning: I'm getting false positives from this approach. In a non-pytest environment, I'm seeing pytest in `sys.modules.keys()`. – duhaime Jun 05 '19 at 00:50
  • 2
    @duhaime The only way that it is in `sys.modules` is that it has been imported, in a way or another, directly in your code or through another import (there might be one of the libraries you use that does import it). And as I said, "this solution only works if the code you're trying to test does not use `pytest` itself". – ramnes Jun 06 '19 at 07:53
  • 1
    Ah, sorry, I didn't see that. I only call `pytest` to run my tests – duhaime Jun 06 '19 at 13:13
  • This is clearly the easiest method – shuaibird Mar 30 '22 at 09:14
  • While this is very simple method, it can lead to false positives. If any part of your code, or any part of any libraries you import, including what those import, import anything from pytest `"pytest" in sys.modules` will be `True`. I would not trust in this. – Niko Föhr Jun 07 '23 at 15:13
  • That's already written in the answer. :) – ramnes Jun 08 '23 at 17:52
96

There's also another way documented in the manual: https://docs.pytest.org/en/latest/example/simple.html#pytest-current-test-environment-variable

Pytest will set the following environment variable PYTEST_CURRENT_TEST.

Checking the existence of said variable should reliably allow one to detect if code is being executed from within the umbrella of pytest.

import os
if "PYTEST_CURRENT_TEST" in os.environ:
    # We are running under pytest, act accordingly...

Note

  • This method works only when an actual test is being run.
  • This detection will not work when modules are imported during pytest collection.
Marek Grzenkowicz
  • 17,024
  • 9
  • 81
  • 111
cgons
  • 1,109
  • 8
  • 6
  • Works for me. (in `env.py` for Flask-Migrate==2.5.2 alembic==1.3.3 Flask-SQLAlchemy==2.4.1 SQLAlchemy==1.3.13 pytest==5.3.5) – techniao Mar 29 '20 at 01:57
  • 21
    Note that this method works only when some actual test is being run! This detection will not work when modules are imported during pytest collection! – kolypto Oct 28 '21 at 22:39
  • 3
    Thanks, @kolypto! That explains why module-level "constants" were set incorrectly for me (as if no test was being run). The answer should include this important fact. – bers Feb 02 '22 at 13:45
  • 1
    As I found out from my short investigation, this variable is not available during the execution of fixtures. Thus, you cannot make a decision about choosing a test database to initialize it. – Konstantin Smolyanin Dec 09 '22 at 16:25
  • Nice, as prod code can avoid a `pytest` import dependancy – dsz Jan 17 '23 at 22:46
49

A solution came from RTFM, although not in an obvious place. The manual also had an error in code, corrected below.

Detect if running from within a pytest run

Usually it is a bad idea to make application code behave differently if called from a test. But if you absolutely must find out if your application code is running from a test you can do something like this:

# content of conftest.py
def pytest_configure(config):
    import sys
    sys._called_from_test = True

def pytest_unconfigure(config):
    import sys  # This was missing from the manual
    del sys._called_from_test

and then check for the sys._called_from_test flag:

if hasattr(sys, '_called_from_test'):
    # called from within a test run
else:
    # called "normally"

accordingly in your application. It’s also a good idea to use your own application module rather than sys for handling flag.

ndclt
  • 2,590
  • 2
  • 12
  • 26
Laizer
  • 5,932
  • 7
  • 46
  • 73
  • 4
    If you found an error in the manual code, please submit a patch. The community will thank you. :) – Bruno Oliveira Aug 07 '14 at 21:36
  • 1
    Done. https://bitbucket.org/LevIsrael/pytest/pull-request/1/fix-example-code-in-detect-if-running-from/diff – Laizer Aug 07 '14 at 22:08
  • 2
    The docs are now updated, even if the PR linked above is not closed. This is probably due to pytest being on GitHub now, not bitbucket. – boxed Oct 16 '18 at 07:26
  • 3
    This may not work - in certain cases conftest.py is loaded too late. See https://github.com/pytest-dev/pytest-django/issues/333 – Almenon Jun 17 '20 at 23:40
  • 1
    What happens if it crashes before `pytest_unconfigure` is called? – evgeni tsvetanov Aug 26 '22 at 09:11
12

Working with pytest==4.3.1 the methods above failed, so I just went old school and checked with:

script_name = os.path.basename(sys.argv[0])
if script_name in ['pytest', 'py.test']:
  print('Running with pytest!')
Greg Dubicki
  • 5,983
  • 3
  • 55
  • 68
duhaime
  • 25,611
  • 17
  • 169
  • 224
  • If you're using 'py.test', this will test for both ```any(re.findall(r'pytest|py.test', sys.argv[0]))``` – naaman Dec 15 '19 at 05:27
  • It seems that the env variable was introduced in 3.2: https://docs.pytest.org/en/stable/changelog.html#pytest-3-2-0-2017-07-30 but maybe it was temporarily removed in some versions ? – smarie Apr 07 '21 at 16:08
  • I found that this is not always reliable. If you use pytest-xdist package this does not work. The module check works in that scenario though – Ted Elliott Oct 18 '21 at 16:11
  • Yes, you should definitely use the module approach unless you get the false positives I describe in a comment on that answer suggestion! – duhaime Oct 19 '21 at 15:08
2

While the hack explained in the other answer (http://pytest.org/latest/example/simple.html#detect-if-running-from-within-a-pytest-run) does indeed work, you could probably design the code in such a way you would not need to do this.

If you design the code to take the database to connect to as an argument somehow, via a connection or something else, then you can simply inject a different argument when you're running the tests then when the application drives this. Your code will end up with less global state and more modulare and reusable. So to me it sounds like an example where testing drives you to design the code better.

Antony Hatchkins
  • 31,947
  • 10
  • 111
  • 111
flub
  • 5,953
  • 27
  • 24
  • 3
    The [py.test documentation](http://pytest.org/latest/example/simple.html#detect-if-running-from-within-a-pytest-run) you referenced is hardly a hack; it's the official solution to a common complaint. That said, the [trivial one-liner proposed by ramnes](https://stackoverflow.com/a/44595269/2809027) is dramatically superior for most use cases. Regardless, **What You Want To Do Is Considered Harmful™** is *never* a valid solution. If that was the best you could do, why bother responding at all? There are demonstrable architectural reasons for a codebase to detect `py.test` at runtime. – Cecil Curry Jan 15 '19 at 23:26
  • 1
    I agree with this concern. In general, if you're writing your code to behave different when run in test, are you really testing your code? Or are you just testing your test code? As you branch your code in different directions when in test, you'll get farther and farther from the spirit of why tests work. #meta – macetw May 25 '21 at 19:08
  • I've found this trick useful for overriding delay times in retry logic when I expect something to fail in my tests. Unit tests should execute fast. – Timo Jun 27 '23 at 12:07
2

This could be done by setting an environment variable inside the testing code. For example, given a project

conftest.py
mypkg/
    __init__.py
    app.py
tests/
    test_app.py

In test_app.py you can add

import os
os.environ['PYTEST_RUNNING'] = 'true'

And then you can check inside app.py:

import os
if os.environ.get('PYTEST_RUNNING', '') == 'true':
    print('pytest is running')
Alex
  • 12,078
  • 6
  • 64
  • 74