[Python] Patching platform.uname in Tests

Posted on in programming

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
from unittest import IsolatedAsyncioTestCase
from unittest.mock import create_autospec, patch

from foo import is_foo, is_bar, platform

class FooTestCase(IsolatedAsyncioTestCase):
    @patch(
        "platform.uname",
        return_value=create_autospec(
            platform.uname_result, system="Linux", node="BAR"
        ),
    )
    def test_wrapper_is_foo(self, m_uname):
        """test is_foo and it's opposite (is_bar)"""
        self.assertFalse(is_foo())
        self.assertTrue(is_bar())

I tend to always use IsolatedAsyncioTestCase - that's probably only necessary if you're testing async code, but maybe I'm lazy :D

I import platform from the library I'm testing rather than importing it directly. This ensures that if something changes with the library (for example, if we switch from using platform to os), we'll immediately notice that we're using something that isn't in the library.

Line 7 is where we begin the patch for platform.uname. If you aren't familiar with how patch works, it's going to create a MagicMock for that function. We're concerned with that that function returns. We set that by setting return_value which is passed to the MagicMock.

In order to ensure that we're not testing too much, we ant to make sure we only set the attributes of the platform.uname return value that we use in our function. In this case, we care about system and node. We use create_autospec to ensure that our return value mimics a platform.uname_result. Additionally, this will ensure our unit test fails if the API changes and one of the attributes we care about doesn't exist any more.

That last sentence is really the important thing here. If we just use a MagicMock, we run the risk of masking an API change. Or, if we actually just use platform.uname_result instead, we run the risk of needlessly breaking a unit test if the API changes in a way that doesn't impact our test (for example, if a required attribute is added).

Check out Autospeccing in the Python docs.

My Bookshelf

Reading Now

Other Stuff