Python unitest: Handle multiple exceptions for code coverage

49 views Asked by At

I have the following class:

class MyClass:
    def _run_command(command):
        # some code
        process = subprocess.Popen(
            command,
            stdout=subprocess.PIPE,
            stderr=subprocess.STDOUT,
        )
        for line in iter(process.stdout.readline, b""):
            decoded_line = line.decode(sys.stdout.encoding).strip()
            output.append(decoded_line)

        try:
            decoded_json = json.loads(decoded_line)
            self._logger.info("Output", extra=decoded_json)
        except JSONDecodeError:
            self._logger.info(decoded_line)
        except TypeError:
            self._logger.info(decoded_line)

Recently the TypeError exception handling was added. Causing the code coverage to not be 100%.

The way I'm doing the test looks like this:

@fixture
def some_fixture():
    return MyClass()

@patch(
    "my_module._MyClass._run_command"
)
@patch("subprocess.Popen")
def test_run_command_failure(
    self,
    mock_popen,
    mock_run_command,
    some_fixture,
):
    test_instance = some_fixture

    mock_popen.return_value.returncode = 1
    mock_popen.return_value.stdout = io.BytesIO(b"test")
    mock_run_command.side_effect=[
        TypeError("run command"),
        JSONDecodeError(msg="run command", doc="x", pos=1),
    ]

    test_command = "test_command"
    test_run_command_result = None
    test_instance._run_command([test_command])

    mock_popen.assert_called_once()
    mock_popen.assert_called_with(
        ["test_command"],
        stdout=subprocess.PIPE,
        stderr=subprocess.STDOUT,
    )
    mock_popen.return_value.wait.assert_called_once()
    assert test_run_command_result.return_code == 1
    assert test_run_command_result.output == ["test"]

I have tried to patch the function and use side_effect for the exceptions, but that causes the mock_popen.assert_called_once() to fail. I'm assuming this is because the mocked function now doesn't contain any of the original implementation, thus, the popen calls are never done.

Since the exceptions are not raised, I can't use with assertRaises(). I'm not sure how can I modify my test to pass and also comply with 100% of the code coverage.

0

There are 0 answers