I have a class that throws an exception when instantiated with wrong values and I would like to write a unit test raising an exception when given wrong parameters.
Instantiating an object outside of the self.assertRaises() doesn't seem to cause any error and I fail to understand why it causes an error when I try to instantiate it within the assert
my class looks like this:
class Mark:
def __init__(self, a, b, c):
try:
self.a = a
self.b = int(b)
self.c = int(c)
except ValueError:
print(e)
My test looks like this:
import unittest
import main
class Test(unittest.TestCase):
def test_marks(self):
self.assertRaises(ValueError, main.Mark("X", 5, 32))
self.assertRaises(ValueError, main.Mark(1, "X", 32))
self.assertRaises(ValueError, main.Mark(1, 5, "X"))
if __name__ == "__main__":
unittest.main()
But I get:
(base) ➜ Code Python3 test.py
E
======================================================================
ERROR: test_marks (__main__.Test)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/Code/test.py", line 12, in test_marks
self.assertRaises(ValueError, main.Mark("X", 5, 32))
File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/unittest/case.py", line 733, in assertRaises
return context.handle('assertRaises', args, kwargs)
File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/unittest/case.py", line 201, in handle
callable_obj(*args, **kwargs)
TypeError: 'Mark' object is not callable
----------------------------------------------------------------------
Ran 1 test in 0.001s
FAILED (errors=1)
Do you know how should I do if I want to test this ?
CodePudding user response:
assertRaises()
expects a callable and not a called function.
You just give the callable and append the arguments without ()
:
self.assertRaises(ValueError, main.Mark, "X", 5, 32)
Alternatively you can use a context manager:
with self.assertRaises(ValueError):
main.Mark("X", 5, 32)
CodePudding user response:
The signature of assertRaises
is different than your usage. See the documentation Here
You should either provide it with a callable and the arguments it should call it with, or you should use the with
statement and call it as usual.
Here is a snippet, showing the two approaches:
def test_marks(self):
self.assertRaises(ValueError, main.Mark, "X", 5, 32)
with self.assertRaises(ValueError):
main.Mark(1, "X", 32)
Based on your edited question: You actually don't throw an exception, but catch it in your code, so the assertion fails, because you don't do what you expect. Tests work!
Now, if you want to check if the exception was caught as expected, you should test if the print statement was printed:
import contextlib
import io
import unittest
import main
class Test(unittest.TestCase):
def test_marks(self):
with contextlib.redirect_stdout(io.StringIO()) as f:
main.Mark(1, 2, 3)
output = f.getvalue().strip()
assert output == 'I am an exception'
if __name__ == "__main__":
unittest.main()
What we do here is to redirect the standard output to a simple text stream, then call your method, capture the output (stripping it to remove any unknown whitespaces) and check if the outputted string is the same as we expect it to be.
In my case, I just print "I am an exception"
. You should replace this with the output of your exception.