How can I mock python's subprocess.check_putput when it makes several different calls

86 views Asked by At

I have a function which makes several subprocess.check_output calls to glean various bits of information. To unit test the code I want to mock the results of check_output depending on the args so I tried this:

@pytest.fixture
def overlay_subprocess_calls(mocker):
    def my_outputs(*args, **kwargs):
        if args:
            print(f"args: {args}, {kwargs}")
            return "called"
            # if args[0] == "readelf":
            #     return fake_readelf
            # elif args[0] == "ldd":
            #     return fake_ldd
            # else:
            #     return fake_firmware_paths

    return mocker.patch("subprocess.check_output", new_callable=my_outputs)

def test_qemu_overlay(tuxrun_args_qemu_overlay, lava_run, overlay_subprocess_calls):
    main()

Have I misunderstood what new_callable is meant to do? I was hoping it would patch in my_outputs but when I run the test I get:

        # work out the loader
>       interp = subprocess.check_output(["readelf", "-p", ".interp", 
qemu_binary]).decode(
            "utf-8"
        )
E       TypeError: 'NoneType' object is not callable

which implies we are returning nothing for the mocked call.

1

There are 1 answers

0
stsquad On

So the two mistakes I made where using new_callable (which returns the object) and not new (which replaces the output of the call). The second error was not using b"" quotes for my strings. The working mocked up function now looks like:

# other strings like this...
fake_firmware_paths = b"""
/usr/share/qemu
/usr/share/seabios
/usr/lib/ipxe/qemu
"""

@pytest.fixture
def overlay_subprocess_calls(mocker):
    def my_outputs(*args, **kwargs):
        if args[0] == "readelf":
            return fake_readelf
        elif args[0] == "ldd":
            return fake_ldd
        else:
            return fake_firmware_paths

    return mocker.patch("subprocess.check_output", new=my_outputs)


def test_qemu_overlay(tuxrun_args_qemu_overlay, lava_run, overlay_subprocess_calls):
    main()