Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

python cross platform testing: mocking os.name

what is the correct way to mock os.name?

I am trying to unittest some cross-platform code that uses os.name to build platform-appropriate strings. I am running on a Windows machine but want to test code that can run on either posix or windows.

I've tried:

production_code.py

from os import name as os_name

def platform_string():
    if 'posix' == os_name:
      return 'posix-y path'
    elif 'nt' == os_name:
      return 'windows-y path'
    else:
      return 'unrecognized OS'

test_code.py

import production as production 
from nose.tools import patch, assert_true

class TestProduction(object):
    def test_platform_string_posix(self):
    """
    """
    with patch.object(os, 'name') as mock_osname:
        mock_osname = 'posix'
        result = production.platform_string()
    assert_true('posix-y path' == result)

this fails because os is not in the global scope for the test_code.py. If 'os' is imported in test_code.py then we will always get os.name=='nt'.

I've also tried:

def test_platform_string_posix(self):
    """
    """
    with patch('os.name', MagicMock(return_value="posix")):
        result = production.platform_string()
    assert_true('posix-y path' == result)

in the test, but this seems not to work because os.name is an attribute not a method with a return value.

EDIT: clarifications in response to comments

  1. The mock docs (1st paragraph) make it seem like directly monkey patching os.name could get messy if e.g. an assertion gets raised
  2. We really are only changing a path based on os.name. Whilst tests will be run on windows and posix machines I wanted something that gave full coverage without needing to resource a machine every time a small edit is made.
like image 215
Laurence Billingham Avatar asked Sep 28 '22 20:09

Laurence Billingham


1 Answers

According to Where to patch you should patch os_name in production_code. By

from os import name as os_name

you are creating a os.name's reference in production_code module called os_name: after that (loaded at import time) change os.name have no effect os_name reference.

class TestProduction(object):
    @patch("production_code.os_name","posix")
    def test_platform_string_posix(self):
        assert_equal('posix-y path', production.platform_string())
like image 105
Michele d'Amico Avatar answered Oct 06 '22 02:10

Michele d'Amico