-1

Suppose I have a function in my main file like this:

# main.py
import time

def little_sleep(num):
   time.sleep(float(f'0.{i}'))

def wait():
    i = 0
    while True:
        try:
            little_sleep(i)
        except KeyboardInterrupt:
            print("Uh Oh!! You really got me there, I guess I'll just have to exit then...")
            break
    return

And I have to PyTest this module and each and every function in it. My question is, how will I test using KeyboardInterrupt without anything happening to other tests and the internal function does not get affected?

# test_main.py
import main
from multiprocessing import Process
import os, time, signal

def test_wait(capfd):
    process = Process(target= main.wait)
    process.start()
    time.sleep(1)
    
    # I tried this but it is sometimes killing the little_sleep() function 
    # and hence the signal is not getting captured by the except 
    os.kill(process.pid, signal.CTRL_C_EVENT)

    # I also tried this but it is directly killing the process without any output and hence is not feasible
    process.kill()

    captured = capfd.readouterr()
    assert captured.out == "Uh Oh!! You really got me there, I guess I'll just have to exit then...\n"

>> pytest test_main.py

collected 1 item
test_main.py F 

=============================== FAILURES ===================================
_______________________________ test_wait __________________________________

capfd = <_pytest.capture.CaptureFixture object at 0x000001F89F7BD400>

    def test_wait(capfd):
        ...
        captured = capfd.readouterr()
>       assert captured.out == "Uh Oh!! You really got me there, I guess I'll just have to exit then...\n"
E       AssertionError: assert '' == 'Uh Oh!! You ...xit then...\n'
E         - Uh Oh!! You really got me there, I guess I'll just have to exit then...

test_main.py:19: AssertionError

Any work arounds on this??

2 Answers2

0

You could mock little_sleep using MagicMock and its side_effect property so that it raises KeyboardInterrupt exception:

import unittest.mock
import main

def test_wait(capfd):
    unittest.mock.MagicMock('main.little_sleep', side_effect=KeyboardInterrupt)
    main.wait()

    captured = capfd.readouterr()
    assert captured.out == "Uh Oh!! You really got me there, I guess I'll just have to exit then...\n"

(untested example)

tmt
  • 7,611
  • 4
  • 32
  • 46
0

You can install signal handler and manage KeyboardInterrupt yourself.

import signal

# Save the default handler and optionally restore it at the end of the test.
default_handler = signal.getsignal(signal.SIGINT)

def handler(signum, frame):
    print('Signal handler called with signal', signum)
   
signal.signal(signal.SIGINT, handler)

See https://docs.python.org/3/library/signal.html

Also, I would put a wider scope for the try...except to make it more robust and efficient:

try:
    while True:
        little_sleep(i)
except KeyboardInterrupt:
        print("Uh Oh!! You really got me there, I guess I'll just have to exit then...")

It may resolve you issue without handling the signal.

Hagai Drory
  • 141
  • 1
  • 5