65 lines
2.1 KiB
Markdown
65 lines
2.1 KiB
Markdown
---
|
|
title: "How to mock stdout runtime attribute of subprocess.Popen - Python ?"
|
|
date: 2018-02-16
|
|
layout: post
|
|
category: Programming
|
|
image: /img/blog/how-to-mock-stdout-runtime-attribute-of-subprocess-popen-python.png
|
|
description: "Everything is in the title ! Subprocess library is hard to test..."
|
|
---
|
|
|
|
[![A missing blog post image](/img/blog/how-to-mock-stdout-runtime-attribute-of-subprocess-popen-python.png)](/img/blog/how-to-mock-stdout-runtime-attribute-of-subprocess-popen-python.png)
|
|
|
|
After 4 days of struggling ~~and having browsed half of StackOverflow answers~~, I've decided to publish a blog post accounting for the Python mocking operation described below.
|
|
|
|
While populating test cases for the [archey project](https://git.io/archey4), I wanted to mock some `subprocess.Popen` calls, used in contexts like this one :
|
|
|
|
{% highlight python %}
|
|
check_output(
|
|
['grep', '-E', '3D|VGA|Display'],
|
|
stdin=Popen(['lspci'],
|
|
stdout=PIPE,
|
|
stderr=DEVNULL).stdout
|
|
).decode().split(': ')[1].rstrip()
|
|
{% endhighlight %}
|
|
|
|
So the first thought you might have would be something like :
|
|
|
|
{% highlight python %}
|
|
@mock.patch.object(
|
|
'module.submodule.Popen',
|
|
'stdout',
|
|
'Mocked string',
|
|
create=True
|
|
)
|
|
{% endhighlight %}
|
|
|
|
... but this does not work, as `Popen` expects a _file-like_ object to read from.
|
|
|
|
In order to sum this mess up, the problems here are :
|
|
|
|
* `subprocess.check_output` call needs a readable pipe to work
|
|
|
|
* `stdout` attribute is **a runtime attribute** of `Popen` return object
|
|
|
|
* The will of not using a different mocking framework than default `unittest`
|
|
|
|
So the final workaround I've come up with is :
|
|
|
|
* Mocking the FIFO with a temporary file
|
|
|
|
* Writing to this file the mocked content
|
|
|
|
* Rewinding to the beginning of the file
|
|
|
|
* Make the `Popen` mock returning this file as `stdout` attribute
|
|
|
|
* Running concerned tests
|
|
|
|
* Deleting the temporary file
|
|
|
|
Everything is more or less explained in this Gist :
|
|
|
|
<script src="https://gist.github.com/HorlogeSkynet/5b1378752669cce829d110fd48c4a5c8.js"></script>
|
|
|
|
Any comment or improvement would be welcome :ok_hand:
|