0

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()
chepner
  • 497,756
  • 71
  • 530
  • 681
Rylie
  • 19
  • 4
  • If you want to test this function, but not the engine, then you have to mock the engine and check that the calls performed on the engine are the correct ones for each case. The input also should be passed externally, as the function is probably doing too much: getting the input and processing it – gontrollez Sep 08 '22 at 12:11
  • @gontrollez So, I should create separate function and then write the unit test for both of them separately – Rylie Sep 08 '22 at 14:50

1 Answers1

0

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
ukBaz
  • 6,985
  • 2
  • 8
  • 31