简体   繁体   中英

How to mock / unit test following code in python?

How would I unit test the following?

def sigterm_handler(signum, frame):
    pid = os.getpid()  # type: int
    sys.exit(0)

signal.signal(signal.SIGTERM, sigterm_handler)

Should I mock and ensure mock is called?

I would write a test that runs your code in a subprocess which can check if you terminated successfully.

For example, let's say your question code lives in a module called signals.py . You can write a test wrapper module that looks like this:

test_signals_wrapper.py

from time import sleep
from sys import exit

# import this last to ensure it overrides any prior settings
import signals

while True:
    sleep(1)

exit(1)  # just in case the loop ends for other reasons

Now you can write a unit test that looks like this:

test_signals.py

from subprocess import run, TimeoutExpired
from sys import executable

def test_sigterm_handler():
    try:
        status = run([executable, '-m', 'test_signals_wrapper'], timeout=30)
    except TimeoutExpired:
        assert False, 'Did not trigger assertion in 30 seconds'

    assert status.returncode == 0, f'Wrong return code: {status.returncode}'

This requires a bit of extra infrastructure for your test, but it solves all the problems with testing this code. By running in a subprocess, you can freely execute sys.exit and get the return value. By having a wrapper script, you can control how the code is loaded and run. You don't need to mock anything, just make sure that your packages are set up correctly, and that your test runner doesn't attempt to pick up the wrapper script as a test.

Monkey patch the handler and send the signal when testing?

import os
import signal
import sys
import time

# your handler
def sigterm_handler(signum, frame):
    print("Handled")
    pid = os.getpid()  # type: int  FIXME: what's this for?
    sys.exit(0)

signal.signal(signal.SIGTERM, sigterm_handler)

# Mock out the existing sigterm_handler
_handled = False
def mocked_sigterm_handler(signum, frame):
    print("Mocked")
    _handled = True

# register the handler
signal.signal(signal.SIGTERM, mocked_sigterm_handler)

# test sending the signal
os.kill(os.getpid(), signal.SIGTERM)
print(f"done ({_handled})")

# reset your handler?
signal.signal(signal.SIGTERM, sigterm_handler)

If you want to test you handler itself you'll probably have to put some kind of code like this.. in the handler which is not beautiful.

if _unittesting_sigterm_handler:
    _handled = True
else:
    sys.exit(0)

and then you can just call the handler directly (or pass the test flag in the call).

_unittesting_sigterm_handler = True
sigterm_handler(0, None)

The code lines you have shown are not suited to be unit-tested, but should rather be integration tested. The reason is, that your code lines consist only of interactions with other components (in this case the signal , sys and os modules).

Therefore, the bugs you can expect to encounter lie in the interactions with these other components: Are you calling the right functions in the right components with the right values for the arguments in the right order and are the results/reactions as you expect them to be?

All these questions can not be answered in a unit-test, where bugs shall be found that can be found in the isolated units: If you mock the signal , sys and/or the os dependencies, then you will write your mocks such that they reflect your (potentially wrong) understanding of these components. The unit-tests will therefore succeed, although the code in the integrated system may fail. If your intent is that the code works on different systems, you might even encounter the situation that the code works in one integration (maybe Linux) but fails in another (maybe Windows).

Therefore, for code like yours, unit-testing and thus mocking for unit-testing does not have much value.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM