Home > Back-end >  In pytest, how to test for sys.exit('some error message')?
In pytest, how to test for sys.exit('some error message')?

Time:12-16

I'm a beginner to python and pytest. I have a function that I'm trying to test for the exceptions part with Pytest:

def read_data(args) -> tuple[Polyline, str]:
    parsed_args = input_parsing_1(args)
    try:
        with open(parsed_args.f) as f_input:
            reader = csv.reader(f_input)
            polyline = fill_poly_with_data(reader)
    except FileNotFoundError as e:
        sys.exit('Invalid input file \n')
    else:
        return (polyline, parsed_args.f)

I want to test if exceptions are risen and if the error message matches the one I put in the code above.

My attempts

@patch('project.open')
def test_read_data_error_SE(mock_open):
    mock_open.side_effect = SystemExit
    with pytest.raises(SystemExit):
        assert read_data(['-f', ''])

@patch('project.open')
def test_read_data_error_FNFE2(mock_open):
    mock_open.side_effect = FileNotFoundError
    with pytest.raises(SystemExit):
        with pytest.raises(FileNotFoundError):
            assert read_data(['-f', 'cos'])

The above tests works fine.

I also wish to assert if sys.exit message matches 'Invalid input file \n'. I've tried:


@patch('project.open')
def test_read_data_error_SE1(mock_open):
    mock_open.side_effect = SystemExit
    with pytest.raises(SystemExit, match='Invalid input file'):
        assert read_data(['-f', ''])

@patch('project.open')
def test_read_data_error_SE2(mock_open):
    mock_open.side_effect = SystemExit
    with pytest.raises(SystemExit) as e:
        assert read_data(['-f', ''])
    assert 'Invalid input file' in str(e.value)

but those tests fails:

===================================================== short test summary info ======================================================
FAILED test_project.py::test_read_data_error_SE1 - AssertionError: Regex pattern did not match.
FAILED test_project.py::test_read_data_error_SE2 - AssertionError: assert 'Invalid input file' in ''

I have seen some posts here on stackoverflow, like: Verify the error code or message from SystemExit in pytest

How to properly assert that an exception gets raised in pytest?

Catch SystemExit message with Pytest

but none of them seems to answer my problem.

My questions:

It seems like my tested message 'Invalid input file' is being matched to an empty string ''? Why? How to properly catch and assert the sys.exit('some error message')?

CodePudding user response:

You're seeing an empty string because you're raising SystemExit with no parameters:

@patch('project.open')
def test_read_data_error_SE1(mock_open):
    mock_open.side_effect = SystemExit
    with pytest.raises(SystemExit, match='Invalid input file'):
        assert read_data(['-f', ''])

You've set mock_open.side_effect = SystemExit, so when you're code calls open(), it raises SystemExit with no parameters. If you want to see a message like Invalid input file, have your code explicitly exit by calling sys.exit('Invalid input file').

Your code already does that, but you're pre-empting that behavior by having the call to open raise SystemExit instead. You probably want open to raise FileNotFound instead:

@patch('project.open')
def test_read_data_error_SE1(mock_open):
    mock_open.side_effect = FileNotFound
    with pytest.raises(SystemExit, match='Invalid input file'):
        assert read_data(['-f', ''])

This will get caught by the except in read_data, which will then call sys.exit.

Here's a complete example (I've included some stub functions here so that I can use your read_data method largely unmodified):

import csv
import sys
import types
import pytest

from unittest import mock

def fill_poly_with_data(x):
    return x

def input_parsing_1(args):
    return types.SimpleNamespace(f="testing")

def read_data(args) -> tuple:
    parsed_args = input_parsing_1(args)
    try:
        with open(parsed_args.f) as f_input:
            reader = csv.reader(f_input)
            polyline = fill_poly_with_data(reader)
    except FileNotFoundError:
        sys.exit('Invalid input file \n')
    else:
        return (polyline, parsed_args.f)

@mock.patch('builtins.open')
def test_function_that_exits(mock_open):
    mock_open.side_effect = FileNotFoundError
    with pytest.raises(SystemExit, match="Invalid input file"):
        read_data(['-f', 'cos'])
  • Related