python cross platform testing: mocking os.name
Question: ## 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 `import`ed 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)](http://mock.readthedocs.org/en/latest/patch.html) 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.


Answer: According to [Where to
patch](https://docs.python.org/3/library/unittest.mock.html#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())
    

