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.