AttributeError: None does not have the attribute 'print' - python

So I am practicing some unit test and I am trying to check for an output that is within a For Loop. Here is my run code
def main():
for i in range(100):
print("Argh!")
Pretty basic, now here is my test code.
import unittest
from unittest import mock # possibly "from unittest import mock" depending on version.
from RunFile import main
class TestMain(unittest.TestCase):
def test_main(self):
with mock.patch.object(main(), 'print') as mock_print:
main()
expected_calls = [mock.call('Argh!') for _ in range(100)]
mock_print.assert_has_calls(expected_calls)
if __name__ == '__main__':
unittest.main()
Here is the error message I get back. I'm not to sure how to resolve this.
UPDATED: Here is the full trace back
======================================================================
ERROR: test_main (__main__.TestMain)
----------------------------------------------------------------------
Traceback (most recent call last):
File "C:/Users/jsalce/Documents/Testsuites/IfStatements/Testsuite.py", line 9, in test_main
with mock.patch.object(RunFile, 'print') as mock_print:
File "C:\Python33\lib\unittest\mock.py", line 1148, in __enter__
original, local = self.get_original()
File "C:\Python33\lib\unittest\mock.py", line 1122, in get_original
"%s does not have the attribute %r" % (target, name)
AttributeError: <module 'RunFile' from 'C:\\Users\\jsalce\\Documents\\Testsuites\\IfStatements\\RunFile.py'> does not have the attribute 'print'
----------------------------------------------------------------------
Ran 1 test in 0.001s
FAILED (errors=1)
Thank you all in advance!

Generally speaking, for mock.patch.object, you want to patch something that you have an easy handle on -- e.g. a module or a class. Usually, you need to patch something that is one level above what you want to replace. For example if you want to patch the foo function in module bar, then you need mock.patch.object(bar, 'foo').
In your case, technically, print is a builtin, but you can patch it on the module where you're using it. This will add a RunFile.print "method" (which is actually a mock) that you can test assertions against. Apparently, since print doesn't actually exist on the module, we need to add create=True to tell mock to create RunFile.print since it doesn't already exist. With that in mind, I'd re-write the unittest as:
import RunFile
class TestMain(unittest.TestCase):
def test_main(self):
with mock.patch.object(RunFile, 'print', create=True) as mock_print:
RunFile.main()
expected_calls = [mock.call('Argh!') for _ in range(100)]
mock_print.assert_has_calls(expected_calls)
if __name__ == '__main__':
unittest.main()

Related

Unexpected behavior from unittest.mock.patch

What is wrong with my code below?
I'm expecting assert call_func_once_with("b") to throw an error as call_func was passed 'a'. I confimed that the function was indeed called once and with argument 'a'.
from unittest.mock import Mock, patch
def call_func(x):
pass
#patch("__main__.call_func")
def test_call_func(call_func):
call_func("a")
assert call_func.called_once_with("b")
assert call_func.called == 1
print(call_func.call_args)
test_call_func()
Output:
call('a')
You're not the first person to notice strange things with these types of assertions (see Magic mock assert_called_once vs assert_called_once_with weird behaviour)
For what it's worth, I can only advise that you try to create a test class which inherits from unittest.TestCase and then use the assertEqual method to get more consistent test behaviour:
import unittest
from unittest.mock import patch, call
def call_func(x):
pass
class MyTests(unittest.TestCase):
#patch("__main__.call_func")
def test_call_func(self, call_func_mock):
call_func_mock("a")
# assert call_func_mock.called == 1
# assert call_func_mock.called_once_with("b")
self.assertEqual(call_func_mock.call_count, 1)
self.assertEqual(call_func_mock.call_args_list[0], call("b"))
print(call_func_mock.call_args)
unittest.main()
This gives the following (expected) results:
F
======================================================================
FAIL: test_call_func (__main__.MyTests)
----------------------------------------------------------------------
Traceback (most recent call last):
File "C:\Python36\lib\unittest\mock.py", line 1179, in patched
return func(*args, **keywargs)
File "C:/scratch.py", line 16, in test_call_func
self.assertEquals(call_func_mock.call_args_list[0], call("b"))
AssertionError: call('a') != call('b')
----------------------------------------------------------------------
Ran 1 test in 0.003s
FAILED (failures=1)
Process finished with exit code 1

(How to Think like a Computer Scientist - 6.3 Unit Testing) Unit Testing using test vs unittest

The sample code in the book uses the test module shown here:
def square(x):
'''raise x to the second power'''
return x * x
import test
print('testing square function')
test.testEqual(square(10), 100)
However, when I write out the script and use IDLE to run it I get the following error:
testing square function
Traceback (most recent call last):
File "/Users/ivan/Documents/scripts/Untitled.py", line 7, in <module>
test.testEqual(square(10), 100)
AttributeError: module 'test' has no attribute 'testEqual'
Checking the Global Module Index > test module shows that the preferred method is to use the unittest module. Here is the given example:
import unittest
class TestStringMethods(unittest.TestCase):
def test_upper(self):
self.assertEqual('foo'.upper(), 'FOO')
def test_isupper(self):
self.assertTrue('FOO'.isupper())
self.assertFalse('Foo'.isupper())
def test_split(self):
s = 'hello world'
self.assertEqual(s.split(), ['hello', 'world'])
# check that s.split fails when the separator is not a string
with self.assertRaises(TypeError):
s.split(2)
if __name__ == '__main__':
unittest.main()
The problem is that we have not learned anything past import unittest as seen in this example.
Is there a way to use the unittest module as given in the first script in this question?
I guess the python book has its own simplified unit testing under the test module.
if you want to create such a simple testing you can create a function like below and call that function.
def testEqual(x,y):
if x==y:
print('Passed')
else:
print('Failed')
testEqual(square(10), 100)

Function is not called using mock.patch

I am trying to test the Class BluetoothClient which connects to a BluetoothSocket. To avoid using real sockets I just want to test that the connect() method from the socket is called with the right parameters. Using mock.patch to replace the imported bluetooth module in my bluetooth_control module doesn't work out like expected.
As I see it, the connect() method is called but the assertion tells me otherwise.
Code:
Unit Under Test (bluetooth_control.py):
import bluetooth
class BluetoothClient(object):
def __init__(self):
self.address="98:D3:31:B2:EF:32"
self.port=1
def establishConnection(self):
self.createSocket()
self.connect()
def createSocket(self):
self.sock=bluetooth.BluetoothSocket( bluetooth.RFCOMM )
def connect(self):
print "connect: sock="+str(self.sock)
self.sock.connect((self.address, self.port))
Test (bluetooth_control_test.py):
import unittest
import mock
import bluetooth_control
import bluetooth
class TestShelf(unittest.TestCase):
def setUp(self):
unittest.TestCase.setUp(self)
self.bc = bluetooth_control.BluetoothClient()
print "setUp"
def tearDown(self):
self.shelf = None
print "tearDown"
#mock.patch('bluetooth_control.bluetooth')
def testEstablishConnection(self,mock_bluetooth):
self.bc.establishConnection()
print "testEstablishConnection sock="+str(self.bc.sock)
mock_bluetooth.connect().assert_called_with(self.bc.sock,("98:D3:31:B2:EF:32",1))
if __name__ == "__main__":
unittest.main()
Output:
setUp
connect: sock=<MagicMock name='bluetooth.BluetoothSocket()' id='140433322111504'>
testEstablishConnection sock=<MagicMock name='bluetooth.BluetoothSocket()' id='140433322111504'>
FtearDown
======================================================================
FAIL: testEstablishConnection (__main__.TestShelf)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/usr/lib/python2.7/site-packages/mock.py", line 1201, in patched
return func(*args, **keywargs)
File "bluetooth_control_test.py", line 21, in testEstablishConnection
mock_bluetooth.connect().assert_called_with(self.bc.sock,("98:D3:31:B2:EF:32",1))
File "/usr/lib/python2.7/site-packages/mock.py", line 831, in assert_called_with
raise AssertionError('Expected call: %s\nNot called' % (expected,))
AssertionError: Expected call: mock(<MagicMock name='bluetooth.BluetoothSocket()' id='140433322111504'>, ('98:D3:31:B2:EF:32', 1))
Not called
----------------------------------------------------------------------
Ran 1 test in 0.003s
FAILED (failures=1)
After looking at the problem again two days later I found the stupid mistakes I made. I had to patch the actual method and remove the falsely added brackets at the assertion.
I am not going to delete this question so that maybe it will help someone to avoid these mistakes.
Test
import unittest
import mock
import bluetooth_control
import bluetooth
class TestShelf(unittest.TestCase):
def setUp(self):
unittest.TestCase.setUp(self)
self.bc = bluetooth_control.BluetoothClient()
print "setUp"
def tearDown(self):
self.shelf = None
print "tearDown"
#mock.patch('bluetooth_control.bluetooth.BluetoothSocket.connect')
def testEstablishConnection(self,mock_connect):
self.bc.establishConnection()
print "testEstablishConnection sock="+str(self.bc.sock)
mock_connect.assert_called_with(("98:D3:31:B2:EF:32",1))
if __name__ == "__main__":
unittest.main()

How to get the exception thrown in unittest

I have my own exceptions and i want to test farther fields in the ex other then the message.
Reading this thread i tried the idea of using a context. I wrote this generic function
def test_runtime_error(test, exception_type, message, display_parameter, path, callable_obj, *args):
pdb.set_trace()
with test.assertRaises(exception_type) as cx:
callable_obj(*args)
ex = cx.exception
test.assertEqual(ex.message,message)
test.assertEqual(ex.display_parameter,display_parameter)
test.assertEqual(ex.path,path)
The path and display_parameter are my own specific fields. I'm not inventing the wheel here, i took most of it from the source.
I'm using it like that
class ExceptionsTest(unittest.TestCase):
def test_something(self):
data = {"name" : "A"}
obj = MyModel.objects.get(pk=1)
test_runtime_error(self,CustomException, 'message', 'A', [], obj.create, data)
The arguments are passed correctly into the callable_obj. the function raises the expected exception. but right after the execution of callable_obj the function breaks and the exception is not fetched. BTW, when i ran the same code in the test it self it worked fine.
Whats wrong here ?
The issue here appears to be this line:
pdb.set_trace()
If you leave it in, but don't have import pdb, the code below will fail with:
E
======================================================================
ERROR: testRaises (__main__.ExceptionTest)
----------------------------------------------------------------------
Traceback (most recent call last):
File "./except.py", line 22, in testRaises
self.verifyComplexException(MyException, 'asdf', RaiseException, 'asdf')
File "./except.py", line 14, in verifyComplexException
pdb.set_trace()
NameError: global name 'pdb' is not defined
----------------------------------------------------------------------
Ran 1 test in 0.000s
FAILED (errors=1)
which matches your description. If you do add the import pdb line, it will drop into the debugger, which is a completely different behavior that cannot be confused for the exit with E or exit with F status, so it can't be that.
Here's a complete example based on this idea which works as intended (licensed under Apache 2.0; see my repo):
import unittest
class MyException(Exception):
def __init__(self, message):
self.message = message
def RaiseException(message):
raise MyException(message)
class ExceptionTest(unittest.TestCase):
def verifyComplexException(self, exception_class, message, callable, *args):
with self.assertRaises(exception_class) as cm:
callable(*args)
exception = cm.exception
self.assertEqual(exception.message, message)
def testRaises(self):
self.verifyComplexException(MyException, 'asdf', RaiseException, 'asdf')
if __name__ == '__main__':
unittest.main()

Running unittest.main() from a module?

I wrote a little function that dynamically defines unittest.TestCase classes (trivial version below).
When I moved it out of the same source file into its own module, I can't figure out how to get unittest to discover the new classes. Calling unittest.main() from either file doesn't execute any tests.
factory.py:
import unittest
_testnum = 0
def test_factory(a, b):
global _testnum
testname = 'dyntest' + str(_testnum)
globals()[testname] = type(testname, (unittest.TestCase,), {'testme': lambda self: self.assertEqual(a, b)})
_testnum += 1
def finish():
unittest.main()
someotherfile.py:
from factory import test_factory, finish
test_factory(1, 1)
test_factory(1, 2)
if __name__ == '__main__':
finish()
Output:
----------------------------------------------------------------------
Ran 0 tests in 0.000s
OK
So it doesn't execute any tests.
Note that keeping it all in the same file works as expected:
import unittest
_testnum = 0
def test_factory(a, b):
global _testnum
testname = 'dyntest' + str(_testnum)
globals()[testname] = type(testname, (unittest.TestCase,), {'testme': lambda self: self.assertEqual(a, b)})
_testnum += 1
test_factory(1, 1)
test_factory(1, 2)
if __name__ == '__main__':
unittest.main()
Output (as expected):
.F
======================================================================
FAIL: testme (__main__.dyntest1)
----------------------------------------------------------------------
Traceback (most recent call last):
File "partb.py", line 11, in <lambda>
globals()[testname] = type(testname, (unittest.TestCase,), {'testme': lambda self: self.assertEqual(a, b)})
AssertionError: 1 != 2
----------------------------------------------------------------------
Ran 2 tests in 0.008s
FAILED (failures=1)
How I use my test_factory() function such that I can execute all of the TestCase objects it defines from a separate source file?
The general idea (what unittest.main does for you) is:
suite = unittest.TestLoader().loadTestsFromTestCase(SomeTestCase)
unittest.TextTestRunner(verbosity=2).run(suite)
as per http://docs.python.org/library/unittest.html?highlight=unittest#module-unittest . Your test cases are hidden in globals() by the test_factory function, so just do a dir(), find the globals that are instances of unittest.TestCase (or ones with names starting with 'dyntest', etc), and just build your suite that way and run it.
By default, unittest.main() looks for unit TestCase objects in the main module. The test_factory creates the TestCase objects in its own module. That's why moving it outside of the main module causes the behavior you see.
Try:
def finish():
unittest.main(module=__name__)

Categories