Assertion Error: Custom Exception not raised
Question:
I am trying to write unittests for a function I wrote that includes a try/except
block.
I have raised a custom exception if my string contains less than 3 characters. I wanted to test that the error gets raised when inputting a string less than 3 characters long.
When running the function, if I input a string less than 3 characters, e.g. "ha" – I get the correct error message: "There are not enough letters in your sentence" which leads me to believe that I have raised the custom exception correctly, however, googling has told me that this means I have not raised my custom exception in my function. I just cannot see or understand where I have gone wrong.
Function file:
from collections import Counter
# set custom exception to raise when a word with less than 3 letters is given
class NotEnoughLetters(Exception):
pass
# create a function that will return the 3 most common letters
def three_most_common(string: str):
string = string.replace(" ", "")
try:
if not all(char.isalpha() for char in string):
raise ValueError("Your input must contain a string") # using all because in this instance I haven't accounted for strings and ints mixed
if len(string) < 3:
raise NotEnoughLetters("There are not enough letters in your sentence")
most_common = Counter(string).most_common(3)
letters = [key for key, value in most_common]
except ValueError as err:
return err
except NotEnoughLetters as e:
return e
else:
return f"Here are your most common letters: 1) {letters[0]} 2) {letters[1]} 3) {letters[2]}"
finally:
print("The program is running, please wait for your output")
Test file:
import unittest
from unittest import TestCase
from common_letters import three_most_common, NotEnoughLetters
class TestCommonLetters(TestCase):
# valid input
def test_good_string(self):
expected_input = "cheesy puff"
expected_output = "Here are your most common letters: 1) e 2) f 3) c"
result = three_most_common(expected_input)
self.assertEqual(expected_output, result) # add assertion here
# invalid input
def test_bad_string(self):
expected_input = "cheesy puff"
false_output = "Here are your most common letters: 1) f 2) f 3) e"
result = three_most_common(expected_input)
self.assertNotEqual(false_output, result)
# edge case 1, having 3 letters
def test_having_three_letters(self):
expected_input = "hay"
expected_output = "Here are your most common letters: 1) h 2) a 3) y"
result = three_most_common(expected_input)
self.assertEqual(expected_output, result)
# edge case 2, having 2 letters TODO this didn't work so get clarification tomorrow as to why not
def test_having_two_letters(self):
with self.assertRaises(NotEnoughLetters):
three_most_common(string="ha")
if __name__ == '__main__':
unittest.main()
This is giving me the following output:
Traceback (most recent call last):
File "C:Homework-LivvyWHomework-LivvyWHomework_week7-LivvyWtest_common_letters.py", line 31, in test_having_two_letters
with self.assertRaises(NotEnoughLetters):
AssertionError: NotEnoughLetters not raised
I have tried to look at similar stackoverflow question/answers but sadly still not comprehending why/where I have gone wrong. Thank you!
Answers:
You are not raising exception because of this block:
except NotEnoughLetters as e:
return e
Instead you are catching it and than just returning it. Remove this block and it will be raised. The same is correct for ValueError.
With these changes your code becomes:
from collections import Counter
# set custom exception to raise when a word with less than 3 letters is given
class NotEnoughLetters(Exception):
pass
# create a function that will return the 3 most common letters
def three_most_common(string: str):
string = string.replace(" ", "")
try:
if not all(char.isalpha() for char in string):
raise ValueError("Your input must contain a string") # using all because in this instance I haven't accounted for strings and ints mixed
if len(string) < 3:
raise NotEnoughLetters("There are not enough letters in your sentence")
most_common = Counter(string).most_common(3)
letters = [key for key, value in most_common]
return f"Here are your most common letters: 1) {letters[0]} 2) {letters[1]} 3) {letters[2]}"
finally:
print("The program is running, please wait for your output")
With these modifications all your 4 tests pass.
Not raise the Custom Exceptions
An other possibility is to remove your custom Exceptions an return directly the error message. So your code becomes:
from collections import Counter
# create a function that will return the 3 most common letters
def three_most_common(string: str):
string = string.replace(" ", "")
try:
if not all(char.isalpha() for char in string):
#raise ValueError("Your input must contain a string") # using all because in this instance I haven't accounted for strings and ints mixed
return "Your input must contain a string"
if len(string) < 3:
#raise NotEnoughLetters("There are not enough letters in your sentence")
return "There are not enough letters in your sentence"
most_common = Counter(string).most_common(3)
letters = [key for key, value in most_common]
#except ValueError as err:
# return err
#except NotEnoughLetters as e:
# return e
#else:
return f"Here are your most common letters: 1) {letters[0]} 2) {letters[1]} 3) {letters[2]}"
finally:
print("The program is running, please wait for your output")
How to change the test code
Accordingly the test code becomes the following:
import unittest
from unittest import TestCase
#from common_letters import three_most_common, NotEnoughLetters
from common_letters import three_most_common
class TestCommonLetters(TestCase):
# valid input
def test_good_string(self):
expected_input = "cheesy puff"
expected_output = "Here are your most common letters: 1) e 2) f 3) c"
result = three_most_common(expected_input)
self.assertEqual(expected_output, result) # add assertion here
# invalid input
def test_bad_string(self):
expected_input = "cheesy puff"
false_output = "Here are your most common letters: 1) f 2) f 3) e"
result = three_most_common(expected_input)
self.assertNotEqual(false_output, result)
# edge case 1, having 3 letters
def test_having_three_letters(self):
expected_input = "hay"
expected_output = "Here are your most common letters: 1) h 2) a 3) y"
result = three_most_common(expected_input)
self.assertEqual(expected_output, result)
# edge case 2, having 2 letters TODO this didn't work so get clarification tomorrow as to why not
"""def test_having_two_letters(self):
with self.assertRaises(NotEnoughLetters):
three_most_common(string="ha")"""
# edge case 2, having 2 letters TODO this didn't work so get clarification tomorrow as to why not
def test_having_two_letters(self):
result = three_most_common(string="ha")
self.assertEqual("There are not enough letters in your sentence", result)
if __name__ == '__main__':
unittest.main()
In the test code, the test case test_having_two_letters()
verifies the error message returned by the function three_most_common()
(as in the other test cases) and not if it is raised an Exception.
I am trying to write unittests for a function I wrote that includes a try/except
block.
I have raised a custom exception if my string contains less than 3 characters. I wanted to test that the error gets raised when inputting a string less than 3 characters long.
When running the function, if I input a string less than 3 characters, e.g. "ha" – I get the correct error message: "There are not enough letters in your sentence" which leads me to believe that I have raised the custom exception correctly, however, googling has told me that this means I have not raised my custom exception in my function. I just cannot see or understand where I have gone wrong.
Function file:
from collections import Counter
# set custom exception to raise when a word with less than 3 letters is given
class NotEnoughLetters(Exception):
pass
# create a function that will return the 3 most common letters
def three_most_common(string: str):
string = string.replace(" ", "")
try:
if not all(char.isalpha() for char in string):
raise ValueError("Your input must contain a string") # using all because in this instance I haven't accounted for strings and ints mixed
if len(string) < 3:
raise NotEnoughLetters("There are not enough letters in your sentence")
most_common = Counter(string).most_common(3)
letters = [key for key, value in most_common]
except ValueError as err:
return err
except NotEnoughLetters as e:
return e
else:
return f"Here are your most common letters: 1) {letters[0]} 2) {letters[1]} 3) {letters[2]}"
finally:
print("The program is running, please wait for your output")
Test file:
import unittest
from unittest import TestCase
from common_letters import three_most_common, NotEnoughLetters
class TestCommonLetters(TestCase):
# valid input
def test_good_string(self):
expected_input = "cheesy puff"
expected_output = "Here are your most common letters: 1) e 2) f 3) c"
result = three_most_common(expected_input)
self.assertEqual(expected_output, result) # add assertion here
# invalid input
def test_bad_string(self):
expected_input = "cheesy puff"
false_output = "Here are your most common letters: 1) f 2) f 3) e"
result = three_most_common(expected_input)
self.assertNotEqual(false_output, result)
# edge case 1, having 3 letters
def test_having_three_letters(self):
expected_input = "hay"
expected_output = "Here are your most common letters: 1) h 2) a 3) y"
result = three_most_common(expected_input)
self.assertEqual(expected_output, result)
# edge case 2, having 2 letters TODO this didn't work so get clarification tomorrow as to why not
def test_having_two_letters(self):
with self.assertRaises(NotEnoughLetters):
three_most_common(string="ha")
if __name__ == '__main__':
unittest.main()
This is giving me the following output:
Traceback (most recent call last):
File "C:Homework-LivvyWHomework-LivvyWHomework_week7-LivvyWtest_common_letters.py", line 31, in test_having_two_letters
with self.assertRaises(NotEnoughLetters):
AssertionError: NotEnoughLetters not raised
I have tried to look at similar stackoverflow question/answers but sadly still not comprehending why/where I have gone wrong. Thank you!
You are not raising exception because of this block:
except NotEnoughLetters as e:
return e
Instead you are catching it and than just returning it. Remove this block and it will be raised. The same is correct for ValueError.
With these changes your code becomes:
from collections import Counter
# set custom exception to raise when a word with less than 3 letters is given
class NotEnoughLetters(Exception):
pass
# create a function that will return the 3 most common letters
def three_most_common(string: str):
string = string.replace(" ", "")
try:
if not all(char.isalpha() for char in string):
raise ValueError("Your input must contain a string") # using all because in this instance I haven't accounted for strings and ints mixed
if len(string) < 3:
raise NotEnoughLetters("There are not enough letters in your sentence")
most_common = Counter(string).most_common(3)
letters = [key for key, value in most_common]
return f"Here are your most common letters: 1) {letters[0]} 2) {letters[1]} 3) {letters[2]}"
finally:
print("The program is running, please wait for your output")
With these modifications all your 4 tests pass.
Not raise the Custom Exceptions
An other possibility is to remove your custom Exceptions an return directly the error message. So your code becomes:
from collections import Counter
# create a function that will return the 3 most common letters
def three_most_common(string: str):
string = string.replace(" ", "")
try:
if not all(char.isalpha() for char in string):
#raise ValueError("Your input must contain a string") # using all because in this instance I haven't accounted for strings and ints mixed
return "Your input must contain a string"
if len(string) < 3:
#raise NotEnoughLetters("There are not enough letters in your sentence")
return "There are not enough letters in your sentence"
most_common = Counter(string).most_common(3)
letters = [key for key, value in most_common]
#except ValueError as err:
# return err
#except NotEnoughLetters as e:
# return e
#else:
return f"Here are your most common letters: 1) {letters[0]} 2) {letters[1]} 3) {letters[2]}"
finally:
print("The program is running, please wait for your output")
How to change the test code
Accordingly the test code becomes the following:
import unittest
from unittest import TestCase
#from common_letters import three_most_common, NotEnoughLetters
from common_letters import three_most_common
class TestCommonLetters(TestCase):
# valid input
def test_good_string(self):
expected_input = "cheesy puff"
expected_output = "Here are your most common letters: 1) e 2) f 3) c"
result = three_most_common(expected_input)
self.assertEqual(expected_output, result) # add assertion here
# invalid input
def test_bad_string(self):
expected_input = "cheesy puff"
false_output = "Here are your most common letters: 1) f 2) f 3) e"
result = three_most_common(expected_input)
self.assertNotEqual(false_output, result)
# edge case 1, having 3 letters
def test_having_three_letters(self):
expected_input = "hay"
expected_output = "Here are your most common letters: 1) h 2) a 3) y"
result = three_most_common(expected_input)
self.assertEqual(expected_output, result)
# edge case 2, having 2 letters TODO this didn't work so get clarification tomorrow as to why not
"""def test_having_two_letters(self):
with self.assertRaises(NotEnoughLetters):
three_most_common(string="ha")"""
# edge case 2, having 2 letters TODO this didn't work so get clarification tomorrow as to why not
def test_having_two_letters(self):
result = three_most_common(string="ha")
self.assertEqual("There are not enough letters in your sentence", result)
if __name__ == '__main__':
unittest.main()
In the test code, the test case test_having_two_letters()
verifies the error message returned by the function three_most_common()
(as in the other test cases) and not if it is raised an Exception.