Search code examples
pythonunit-testingmockingpython-unittestpyttsx3

Writing unit test for a dependent function


I have this function that asks the user for the gender of the voice and then reads the text in that voice. If the user input is not either male or female it raises a value error. I want to write a unit test for this function, but don't know how exactly I can do that.

def pdftospeech(n):
    readersvoice = input("Voice: ").lower().strip()

    voices = engine.getProperty("voices")

    if readersvoice == "female":
        engine.setProperty("voice", voices[1].id)
    elif readersvoice == "male":
        engine.setProperty("voice", voices[0].id)
    else:
        raise ValueError("Choose from male or female")

    engine.setProperty("rate", 150)
    rate = engine.getProperty("rate")

    engine.say(n)
    engine.runAndWait()

Solution

  • My recommendation would be to split out the asking and checking of input from the getting and setting of properties on the engine.

    For example:

    def ask_voice():
        readers_voice = input("Voice: ").lower().strip()
        if readers_voice not in ["female", "male"]:
            raise ValueError("Choose from male or female")
        return readers_voice
    
    def read_with_voice(n, readers_voice):
        voices = engine.getProperty("voices")
    
        if readers_voice == "female":
            engine.setProperty("voice", voices[1].id)
        elif readers_voice == "male":
            engine.setProperty("voice", voices[0].id)
    
        engine.setProperty("rate", 150)
        rate = engine.getProperty("rate")
    
        engine.say(n)
        engine.runAndWait()
    
    def pdftospeech(n):
        readers_voice = ask_voice()
        read_with_voice(n, readers_voice)
    
    

    The test for ask_voice is then more straightforward:

    import unittest
    from unittest import mock
    
    
    def ask_voice():
        readers_voice = input("Voice: ").lower().strip()
        if readers_voice not in ["female", "male"]:
            raise ValueError("Choose from male or female")
        return readers_voice
    
    
    class Test101(unittest.TestCase):
    
        def test_ask_male(self):
            with mock.patch('builtins.input', return_value="MaLe"):
                self.assertEqual('male', ask_voice())
    
        def test_ask_female(self):
            with mock.patch('builtins.input', return_value="FeMaLe"):
                self.assertEqual('female', ask_voice())
    
        def test_ask_fail(self):
            with mock.patch('builtins.input', return_value="mail"):
                self.assertRaises(ValueError, ask_voice)
    
    
    if __name__ == '__main__':
        unittest.main(verbosity=2)
    

    This test gave the following output:

    test_ask_fail (__main__.Test101) ... ok
    test_ask_female (__main__.Test101) ... ok
    test_ask_male (__main__.Test101) ... ok
    
    ----------------------------------------------------------------------
    Ran 3 tests in 0.001s
    
    OK