Pass Raised Error in function to Exception Raised by UnitTest [duplicate] - python

This question already has answers here:
How to write a custom `.assertFoo()` method in Python?
(3 answers)
Closed 7 years ago.
I would like to write a function that raises an error when it fails and then passes that to unittest. Consider the following:
class undefinedTerms(unittest.TestCase):
def setUp(self):
self.A = frozenset((1,2,3,2,1,4,2,5,6,3,5,7,1,5,2,4,8))
self.B = frozenset((1,4,5,6,3,4,2,5,4,3,1,3,4,2,5,3,6,7,4,2,3,1))
self.C = frozenset((1,2,3,2,1,4,2,5,6,3,5,7,1,5,2,4))
self.D = (1,2,1)
def is_a_set(self,set_this):
try:
assert isinstance(set_this, (set,frozenset))
except TypeError:
raise TypeError("The object you passed is not a set.")
return True
def test_A_is_a_set(self):
self.assertTrue(self.is_a_set(self.A))
def test_B_is_a_set(self):
self.assertTrue(self.is_a_set(self.B))
def test_C_is_a_set(self):
self.assertTrue(self.is_a_set(self.C))
def test_D_is_a_set(self):
self.assertTrue(self.is_a_set(self.D), self.is_a_set(self.D))
suite = loader.loadTestsFromTestCase(undefinedTerms)
unittest.TextTestRunner(verbosity=2).run(suite)
This gives the following output.
test_A_is_a_set (__main__.undefinedTerms) ... ok
test_B_is_a_set (__main__.undefinedTerms) ... ok
test_C_is_a_set (__main__.undefinedTerms) ... ok
test_D_is_a_set (__main__.undefinedTerms) ... FAIL
======================================================================
FAIL: test_D_is_a_set (__main__.undefinedTerms)
----------------------------------------------------------------------
Traceback (most recent call last):
File "<ipython-input-33-4751f8653e7a>", line 27, in test_D_is_a_set
self.assertTrue(self.is_a_set(self.D), self.is_a_set(self.D))
File "<ipython-input-33-4751f8653e7a>", line 12, in is_a_set
(type(set_this) is set))
AssertionError
----------------------------------------------------------------------
Ran 4 tests in 0.001s
FAILED (failures=1)
What I would like is for the AssertionError to be the TypeError defined in the function. I am open to radically different implementations.
Update
I think I was unclear as to precisely what I am after. After reading comments and answers I believe what I want is to create a custom assertion.
This has previously been addressed here.

How about using .assertIsInstance()
def assert_is_set(self, set_this):
self.assertIsInstance(set_this, (set, frozenset))
At this point, you don't really need a function and can just inline the check.
You can also use the more general .fail() method if your conditions become more complex.
Signals a test failure unconditionally, with msg or None for the error message
if something:
self.fail("Computer broken!")
The documentation page has a list of all the assertions available in TestCases

As proposed here, I believe what I was after is custom assertion methods. This is the final implementation I went with.
class SetAssertions:
def assertIsASet(self, set_this):
if not isinstance(set_this, (set,frozenset)):
raise AssertionError("The object you passed is not a set.")
import unittest
class undefinedTerms(unittest.TestCase, SetAssertions):
def setUp(self):
self.A = frozenset((1,2,3,2,1,4,2,5,6,3,5,7,1,5,2,4,8))
self.B = frozenset((1,4,5,6,3,4,2,5,4,3,1,3,4,2,5,3,6,7,4,2,3,1))
self.C = frozenset((1,2,3,2,1,4,2,5,6,3,5,7,1,5,2,4))
self.D = (1,2,1)
def test_A_is_a_set(self):
self.assertIsASet(self.A)
def test_B_is_a_set(self):
self.assertIsASet(self.B)
def test_C_is_a_set(self):
self.assertIsASet(self.C)
def test_D_is_a_set(self):
self.assertIsASet(self.D)
suite = loader.loadTestsFromTestCase(undefinedTerms)
unittest.TextTestRunner(verbosity=2).run(suite)
test_A_is_a_set (__main__.undefinedTerms) ... ok
test_B_is_a_set (__main__.undefinedTerms) ... ok
test_C_is_a_set (__main__.undefinedTerms) ... ok
test_D_is_a_set (__main__.undefinedTerms) ... FAIL
======================================================================
FAIL: test_D_is_a_set (__main__.undefinedTerms)
----------------------------------------------------------------------
Traceback (most recent call last):
File "<ipython-input-39-495718fb2bad>", line 32, in test_D_is_a_set
self.assertIsASet(self.D)
File "<ipython-input-39-495718fb2bad>", line 6, in assertIsASet
raise AssertionError("The object you passed is not a set.")
AssertionError: The object you passed is not a set.
----------------------------------------------------------------------
Ran 4 tests in 0.002s
FAILED (failures=1)

Related

Error generated when running unit test 'None'

I am new to python and trying to run a unit test. My function works well when using the print method, but while trying to develop a test module I keep getting errors.
my function in the python file(work.py)
def lee(n):
for i in range(1,n+1):
print (i)
my unit test module
import unittest
import work
class TestWork(unittest.TestCase):
def test_lee(self):
result = work.lee(3)
self.assertEqual(result, [1,2,3])
if __name__ == '__main__':
unittest.main()
errors generated
======================================================================
FAIL: test_lee (__main__.TestWork)
----------------------------------------------------------------------
Traceback (most recent call last):
File "C:\Users\test_work.py", line
12, in test_lee
self.assertEqual(result, [1,2,3])
AssertionError: None != [1, 2, 3]
Here,
def lee(n):
list = []
for i in range(1,n+1):
list.append(i)
return list
As for printing, do print(work.lee(3))

How to run unit tests in python?

I am having a difficult time understanding how to use assertions in unit tests in Python.
Original code:
class A:
def method(file):
if file:
<do something>
else:
raise Exception("file not found")
Now to create its test. Let's say I don't want to pass a file and test it.
t1 = A()
class Test(TestCase):
def test_method_no_path(self):
t1.method(' ') #passed no file
<Now do what> ??
self.assert ??
# tests/test_ex.py
from os import path
from unittest import TestCase
class Error(Exception):
pass
class FileChecker:
def process_file(self, f_path: str):
if path.exists(f_path) and path.isfile(f_path):
# just an example
return 'result'
raise Error(f_path)
class TestFileChecker(TestCase):
_CHECKER = FileChecker()
def test_done(self):
# check method result
self.assertTrue(self._CHECKER.process_file('/tmp/1.txt') == 'result')
def test_error(self):
# check method exception
with self.assertRaises(Error):
self._CHECKER.process_file('/tmp/1.txt')
Run our test(nosetests tests/test_ex.py). test_done failed because /tmp/1.txt file does not exist:
======================================================================
ERROR: test_done (tests.test_ex.TestFileChecker)
Let's create a file(echo 'test' >> /tmp/1.txt) and run test one more time:
======================================================================
FAIL: test_error (tests.test_ex.TestFileChecker)
As you can see test_done works fine, because we got expected result, but now we have problems with test_error(because Error wasn't raised).

Test a function that contains assert statement and doesn't return anything

If, for example, I have a function like this:
def func(inputs: List):
""" function to test
args: inputs (list of tuples)
"""
for elem in inputs:
assert elem[0].attr1 == elem[1].attr1
assert elem[0].attr2 == elem[1].attr2
if hasttr(elem[0], "attr3") & hasttr(elem[1], "attr3"):
assert elem[0].attr3 == elem[1].attr3
How can I write the test for this function using pytest?
I know we can use this code (source: https://docs.pytest.org/en/stable/assert.html):
import pytest
def myfunc():
raise ValueError("Exception 123 raised")
def test_match():
with pytest.raises(ValueError, match=r".* 123 .*"):
myfunc()
to test a function that raise a ValueError, but what about assert?
Thank you!
As per one of the comments, you can simply check for AssertionError:
import pytest
def myfunc():
# some code
assert 0 == 1, "boom" # raises AssertionError
def test_myfunc():
with pytest.raises(AssertionError, match=r".*boom.*"):
myfunc()
If an AssertionError hasn't been raised by myfunc(), test_myfunc() will fail with:
Failed: DID NOT RAISE <class 'AssertionError'>
If an AssertionError with a different message has been raised by myfunc(), the test will fail with:
AssertionError: Regex pattern ... does not match ...

Problems mocking nested classes in DNSResolver

I have the following code:
import unittest, mock
class MockedRRData(object):
def to_text(self):
return '0 example.com.'
class MockedResponse(object):
answer = [[MockedRRData()]]
class MockedReturnValue(object):
response = MockedResponse()
class MockedDNSResolver(object):
def query(self, domain_name, query_type):
return MockedReturnValue()
class DNSQueryTest(unittest.TestCase):
def setUp(self):
# MockedRRData = mock.Mock(to_text=lambda: '0 example.com.')
# MockedResponse = mock.Mock(answer=[[MockedRRData()]])
# MockedReturnValue = mock.Mock()
# MockedReturnValue.attach_mock(MockedResponse, 'response')
# MockedReturnValue = mock.Mock(response=MockedResponse())
self.fake_dns_resolver = mock.Mock(query=lambda *args, **kwargs: MockedReturnValue())
def test_mock(self):
for rrset in self.fake_dns_resolver.query('a', 'b').response.answer:
for rrdata in rrset:
print(rrdata.to_text())
unittest.main()
It works, but I'd like to transition to fully using mock classes - as shown in the comments. The problem is that when I uncomment even just the last line of my comments, I get the following:
E
======================================================================
ERROR: test_mock (__main__.DNSQueryTest)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/a.py", line 33, in test_mock
for rrset in self.fake_dns_resolver.query('a', 'b').response.answer:
TypeError: 'Mock' object is not iterable
----------------------------------------------------------------------
Ran 1 test in 0.000s
FAILED (errors=1)
How do I fix that?
Apparently, I made the mistake of trying to instantiate mocks twice by adding extra () (mock.Mock returns an instance, not a class template). Here's the working code - compare the previously commented part.
import unittest, mock
class MockedRRData(object):
def to_text(self):
return '0 profound.mail.pairserver.com.'
class MockedResponse(object):
answer = [[MockedRRData()]]
class MockedReturnValue(object):
response = MockedResponse()
class MockedDNSResolver(object):
def query(self, domain_name, query_type):
return MockedReturnValue()
class DNSQueryTest(unittest.TestCase):
def setUp(self):
MockedRRData = mock.Mock(to_text=lambda: '0 profound.mail.pairserver.com.')
MockedResponse = mock.Mock(answer=[[MockedRRData]])
MockedReturnValue = mock.Mock(response=MockedResponse)
self.fake_dns_resolver = mock.Mock(query=lambda *args, **kwargs: MockedReturnValue)
def test_mock(self):
for rrset in self.fake_dns_resolver.query('a', 'b').response.answer:
for rrdata in rrset:
print(rrdata.to_text())
unittest.main()

Issue with passing class method into a class method in python

i have following python code (a bit simplified, but it did make the same error).
class traffic(object):
def __init__(self, testObj):
try:
<do something>
except AssertionError:
sys.exit (1)
def add(self, phase='TEST'):
<do something>
def check(self, phase='TEST'):
<do something>
class testcase(object):
def __init__(self):
try:
<do something>
except AssertionError:
sys.exit (1)
def addSeqPost(self, cmdObj):
print "add Seq. for POST"
cmdObj(phase='POST')
tc = testcase()
test = traffic(tc)
tc.addSeqPost(test.add())
I get the below TypeError:
Traceback (most recent call last):
File "test.py", line 25, in <module>
tc.addSeqPost(test.add())
File "test.py", line 20, in addSeqPost
cmdObj(phase='POST')
TypeError: 'NoneType' object is not callable
If i change my code to, it works, but it is not what i would like:
def addSeqPost(self, cmdObj):
print "add Seq. for POST"
cmdObj.add(phase='POST')
tc.addSeqPost(test())
I would like to make it more general because the test() could have more methods that i would like to pass into tc.addSeqPost(), like tc.addSeqPost(test.check()).
Thanks in adv. for your time and help
After the help from alKid.
One issue remains, what if i want to pass a parameter with test.check(duration=5)? As soon i do that i got the same TypeError...But i don't want/need to return anything from add!!!
Example:
...
def check(self, phase='TEST', duration=0):
<do something>
tc = testcase()
test = traffic(tc)
tc.addSeqPost(test.add)
tc.addSeqPost(test.check(duration=5))
test.add() will not return the function, it runs the function and gives back the returned value. Since add doesn't return anything, the object passed is None.
tc = testcase()
test = traffic(tc)
tc.addSeqPost(test.add)
Also, remember that test.add needs two arguments. self and phase. You need to pass both of them.
def addSeqPost(self, cmdObj):
print "add Seq. for POST"
cmdObj(self, phase='POST') #pass an instance of `testcase` to the function.
Passing another class's instance might not be what you want to do, but it's just an example.
Hope this helps!

Categories