Search code examples
pythonunit-testingmockingpatch

Mocking a global variable


I've been trying to implement some unit tests for a module. An example module named alphabet.py is as follows:

import database

def length_letters():
    return len(letters)

def contains_letter(letter):
    return letter in letters


letters = database.get('letters')   # returns a list of letters

I'd like to mock the response from a database with some values of my choice, but the code below doesn't seem to work.

import unittests  
import alphabet   
from unittest.mock import patch   
  
  
class TestAlphabet(unittest.TestCase): 
    @patch('alphabet.letters')
    def setUp(self, mock_letters):
        mock_letters.return_value = ['a', 'b', 'c']   
  
    def test_length_letters(self):
        self.assertEqual(3, alphabet.length_letters())
      
    def test_contains_letter(self):   
        self.assertTrue(alphabet.contains_letter('a'))

I have seen many examples in which 'patch' is applied to methods and classes, but not to variables. I prefer not to patch the method database.get because I may use it again with different parameters later on, so I would need a different response.

What am I doing wrong here?


Solution

  • Try this:

    import unittests  
    import alphabet   
    from unittest import mock 
    
    
    class TestAlphabet(unittest.TestCase): 
        def setUp(self):
            self.mock_letters = mock.patch.object(
                alphabet, 'letters', return_value=['a', 'b', 'c']
            )
    
        def test_length_letters(self):
            with self.mock_letters:
                self.assertEqual(3, alphabet.length_letters())
    
        def test_contains_letter(self):
            with self.mock_letters:
                self.assertTrue(alphabet.contains_letter('a'))
    

    You need to apply the mock while the individual tests are actually running, not just in setUp(). We can create the mock in setUp(), and apply it later with a with ... Context Manager.