Writing unit test for a dependent function

Question:

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()
Asked By: Rylie

||

Answers:

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
Answered By: ukBaz