Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to mock subprocess.run in pytest?

I have the class InternalProc, defined as following:

class InternalProc:

    @staticmethod
    def get_data():
        try:
            result = subprocess.run(['bridge-client', '--business-credentials'],
                                    stdout=subprocess.PIPE)
            data = json.loads(result.stdout.decode('utf-8'))
            return data
        except Exception:
            logger.error("Unable to fetch the data")
            raise

I want to unit test the get_data(), how should I mock subprocess.run? I also want to assert on whether the exception was raised or not.

like image 812
user8082934 Avatar asked Mar 30 '26 20:03

user8082934


2 Answers

Seems like you need two tests to test this method, one that returns data and one that raises an Exception.

For the one where we return data we simply need to mock subprocess.run. Then we can create another Mock object to mock stdout.decode to return data that json.loads can parse. This means creating a Mock object for the behavior of stdout and then configuring our mocked subprocess.run to use this object.

For the other test we simply need to use the side_effect kwarg of the Mock object to raise an exception when it is called.

Given the following folder structure:

stackoverflow/
├── mypackage
│   ├── __init__.py
│   └── proc.py
└── tests
    ├── __init__.py
    └── test_proc.py

The tests we write are shown below.

from unittest.mock import MagicMock, patch

import pytest

from mypackage.proc import InternalProc


@patch("mypackage.proc.subprocess.run")
def test_get_data_valid(mock_run):
    mock_stdout = MagicMock()
    mock_stdout.configure_mock(
        **{
            "stdout.decode.return_value": '{"A": 3}'
        }
    )

    mock_run.return_value = mock_stdout

    result = InternalProc.get_data()
    assert result == {"A": 3}


@patch("mypackage.proc.subprocess.run", side_effect=Exception("foobar"))
def test_get_data_invalid(mock_run):
    with pytest.raises(Exception) as exc:
        InternalProc.get_data()
        assert "foobar" in str(exc.value)

Output:

======================================= test session starts ========================================
platform darwin -- Python 3.9.1, pytest-6.2.2, py-1.10.0, pluggy-0.13.1
rootdir: ***/stackoverflow
collected 2 items                                                                                  

tests/test_proc.py ..                                                                        [100%]

======================================== 2 passed in 0.07s =========================================

My suggestion to you, since I have seen you post multiple pytest/mock questions in the last few days is to spend some time reading the documentation for both. Make some toy examples and play around with the two packages. That is the only way you will learn how to mock and where to mock.

like image 161
gold_cy Avatar answered Apr 02 '26 13:04

gold_cy


Another solution is pytest-subprocess

like image 33
Waket Zheng Avatar answered Apr 02 '26 13:04

Waket Zheng



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!