I've gone through some of the previous questions on here about mocking sqlite3 in python when doing unit tests, and unfortunately none of them have helped me be able to successfully mock the result from fetchone().
The following is a quick test example I put together to try and get it working:
TETS.PY
import unittest
import sqlite3
from unittest import TestCase, mock
from unittest.mock import patch, MagicMock
class Foo:
def checkActive(self):
conn = sqlite3.connect('lll.db')
cur = conn.execute("SELECT * FROM SQLITE_MASTER")
value = cur.fetchone()
return value
class test_Foo(TestCase):
#patch('tets.sqlite3')
def test_shortTest(self, mock_sql):
mock_sql.connect().cursor().fetchall.return_value = ('Test',)
test_class = Foo()
return_mock = test_class.checkActive()
print(return_mock)
if __name__ == '__main__': # pragma: no cover -> local unittest main call
unittest.main()
I've tried variations of the above, as well as patching tets.sqlite3.connect and going from there but I always either of the below as result:
[Running] python -u "c:\Users\z003uwfm\Desktop\tets.py"
<MagicMock name='connect().execute().fetchone()' id='45622576'>
.
----------------------------------------------------------------------
Ran 1 test in 0.016s
OK
[Running] python -u "c:\Users\z003uwfm\Desktop\tets.py"
None
.
----------------------------------------------------------------------
Ran 1 test in 0.021s
OK
Does anyone have a real working example where they were able to mock the return from either fetchone() or fetchall()?
Thanks!
After further tinkering I found the following to be working:
#patch('sqlite3.connect')
def test_shortTest(self, mock_sql):
mock_sql.return_value.execute.return_value.fetchone.return_value = ('Test',)
test_class = Foo()
return_mock = test_class.checkActive()
print(return_mock)
All the other code remains the same as per the original post. Hope this helps others out if they ever have to come across this!
Related
I am trying to write a unit test that will test azure.storage.blob.BlobServiceClient class and its methods. Below is my code
A fixture in the conftest.py
#pytest.fixture
def mock_BlobServiceClient(mocker):
azure_ContainerClient = mocker.patch("azure.storage.blob.ContainerClient", mocker.MagicMock())
azure_BlobServiceClient= mocker.patch("azure_module.BlobServiceClient", mocker.MagicMock())
azure_BlobServiceClient.from_connection_string.return_value
azure_BlobServiceClient.get_container_client.return_value = azure_ContainerClient
azure_ContainerClient.list_blob_names.return_value = "test"
azure_ContainerClient.get_container_client.list_blobs.return_value = ["test"]
yield azure_BlobServiceClient
Contents of the test file
from azure_module import AzureBlob
def test_AzureBlob(mock_BlobServiceClient):
azure_blob = AzureBlob()
# This assertion passes
mock_BlobServiceClient.from_connection_string.assert_called_once_with("testconnectionstring")
# This assertion fails
mock_BlobServiceClient.get_container_client.assert_called()
Contents of the azure_module.py
from azure.storage.blob import BlobServiceClient
import os
class AzureBlob:
def __init__(self) -> None:
"""Initialize the azure blob"""
self.azure_blob_obj = BlobServiceClient.from_connection_string(os.environ["AZURE_STORAGE_CONNECTION_STRING"])
self.azure_container = self.azure_blob_obj.get_container_client(os.environ["AZURE_CONTAINER_NAME"])
My test fails when I execute it with below error message
> mock_BlobServiceClient.get_container_client.assert_called()
E AssertionError: Expected 'get_container_client' to have been called.
I am not sure why it says that the get_container_client wasn't called when it was called during the AzureBlob's initialization.
Any help is very much appreciated.
Update 1
I believe this is a bug in the unittest's MagicMock itself. Per
Michael Delgado suggested that I dialed the code to a bare minimum to test and identify the issue, and I concluded that the MagicMock was causing the problem. Below are my findings:
conftest.py
#pytest.fixture
def mock_Blob(mocker):
yield mocker.patch("module.BlobServiceClient")
test_azureblob.py
def test_AzureBlob(mock_Blob):
azure_blob = AzureBlob()
print(mock_Blob)
print(mock_Blob.mock_calls)
print(mock_Blob.from_connection_string.mock_calls)
print(mock_Blob.from_connection_string.get_container_client.mock_calls)
assert False # <- Intentional fail
After running the test, I got the following results.
$ pytest -vv
.
.
.
------------------------------------------------------------------------------------------- Captured stdout call -------------------------------------------------------------------------------------------
<MagicMock name='BlobServiceClient' id='140704187870944'>
[call.from_connection_string('AZURE_STORAGE_CONNECTION_STRING'),
call.from_connection_string().get_container_client('AZURE_CONTAINER_NAME')]
[call('AZURE_STORAGE_CONNECTION_STRING'),
call().get_container_client('AZURE_CONTAINER_NAME')]
[]
.
.
.
The prints clearly show that the get_container_client was seen being called, but the mocked method did not register it at its level. That led me to conclude that the MagicMock has a bug which I will report to the developers for further investigation.
I've met an error "fixture 'invalid_email'" not found, while running this test:
import pytest
import time
from ddt import ddt, data, unpack
from pages.home.signup_page import SignUpPage
from utilities.read_data import get_csv_data
#pytest.mark.usefixtures("base_set_up", "set_up")
#ddt
class TestSignUp():
#pytest.fixture(autouse=True)
def class_set_up(self, base_set_up):
self.test_user = SignUpPage(self.driver)
#pytest.mark.run(order=2)
def test_valid_sign_up_with_confirmation(self):
self.test_user.valid_signup()
self.test_user.use_mailer()
result = self.test_user.verify_successful_sign_up()
#pytest.mark.run(order=1)
#data(*get_csv_data('/Users/blabla/utilities/test_data.csv'))
#unpack
def test_invalid_sign_up_invalid_email(self, invalid_email):
self.test_user.invalid_signup(invalid_email)
time.sleep(3)
result = self.test_user.verify_error_sign_up()
What could happen? I've double checked the file test_data.csv for extra spaces, etc.
invalid_email
qqq
111
Error message:
fixture 'invalid_email' not found
available fixtures: base_set_up, browser...
use 'pytest --fixtures [testpath]' for help on them.
I've solved it.
If you click on the decorator, it says:
def data(*values):
"""
Method decorator to add to your test methods.
Should be added to methods of instances of ``unittest.TestCase``.
"""
return idata(values)
So you have to import unittest first, and then do this:
class TestSignUp(unittest.TestCase)
I am trying to use PyTest_Mock in order to do some testing in my Python project. I created a very simple test to try it out, but I am getting an AttributeError and I don't know why.
model.py
def square(x):
return x * x
if __name__ == '__main__':
res = square(5)
print("result: {}".format(res))
test_model.py
import pytest
from pytest_mock import mocker
import model
def test_model():
mocker.patch(square(5))
assert model.square(5) == 25
After running python -m pytest I get a failure and the following error:
def test_model():
> mocker.patch(square(5))
E AttributeError: 'function' object has no attribute 'patch'
test_model.py:7: AttributeError
You don't need to import mocker, it's available as fixture, so you just pass it as a parameter in the test function:
def test_model(mocker):
mocker.patch(...)
square(5) evaluates to 25, so mocker.patch(square(5)) will effectively try to patch a number 25. Instead, pass the function name as parameter: either
mocker.patch('model.square')
or
mocker.patch.object(model, 'square')
Once patched, square(5) will not return 25 anymore since the original function is replaced with a mock object that can return anything and will return a new mock object by default. assert model.square(5) == 25 will thus fail. Usually, you patch stuff either to avoid complex test setup or simulate behaviour of components that is desired in test scenario (for example, a website being unavailable). In your example, you don't need mocking at all.
Complete working example:
import model
def test_model(mocker):
mocker.patch.object(model, 'square', return_value='foo')
assert model.square(5) == 'foo'
I wanted to call setUpClass and tearDownClass so that setup and teardown would be performed only once for each test. However, it keeps failing for me when I call tearDownClass. I only want to record 1 test result, either PASS if both tests passed or FAIL if both tests failed. If I call only setup and tearDown then all works fine:
Calling setUpClass and tearDownClass:
#!/usr/bin/python
import datetime
import itertools
import logging
import os
import sys
import time
import unittest
LOGFILE = 'logfile.txt'
class MyTest(unittest.TestCase):
global testResult
testResult = None
#classmethod
def setUpClass(self):
## test result for DB Entry:
self.dbresult_dict = {
'SCRIPT' : 'MyTest.py',
'RESULT' : testResult,
}
def test1(self):
expected_number = 10
actual_number = 10
self.assertEqual(expected_number, actual_number)
def test2(self):
expected = True
actual = True
self.assertEqual(expected, actual)
def run(self, result=None):
self.testResult = result
unittest.TestCase.run(self, result)
#classmethod
def tearDownClass(self):
ok = self.testResult.wasSuccessful()
errors = self.testResult.errors
failures = self.testResult.failures
if ok:
self.dbresult_dict['RESULT'] = 'Pass'
else:
logging.info(' %d errors and %d failures',
len(errors), len(failures))
self.dbresult_dict['RESULT'] = 'Fail'
if __name__ == '__main__':
logger = logging.getLogger()
logger.addHandler(logging.FileHandler(LOGFILE, mode='a'))
stderr_file = open(LOGFILE, 'a')
runner = unittest.TextTestRunner(verbosity=2, stream=stderr_file, descriptions=True)
itersuite = unittest.TestLoader().loadTestsFromTestCase(MyTest)
runner.run(itersuite)
sys.exit()
unittest.main(module=itersuite, exit=True)
stderr_file.close()
Error:
test1 (__main__.MyTest) ... ok
test2 (__main__.MyTest) ... ok
ERROR
===================================================================
ERROR: tearDownClass (__main__.MyTest)
-------------------------------------------------------------------
Traceback (most recent call last):
File "testTearDownClass.py", line 47, in tearDownClass
ok = self.testResult.wasSuccessful()
AttributeError: type object 'MyTest' has no attribute 'testResult'
----------------------------------------------------------------------
Ran 2 tests in 0.006s
FAILED (errors=1)
like #Marcin already pointed out, you're using the Unittest-Framework in a way it isn't intended.
To see if the tests are successful you check the given values with the expected, like you already did: assertEqual(given, expected). Unittest will then collect a summary of failed ones. you don't have to do this manually.
If you want to check that two tests need to be together successful or fail together, these should be combined in ONE Test, maybe as a additionally one, if the individual Tests need to be checked as well. This is nothing you want to save and load afterwards. The tests itself should be as stateless as possible.
When you say you want to run the SetUp and TearDown 'once per test', do you mean once per test-method or per test-run? This is different if you have more than one test-method inside your class:
setUp() Will be called before each test-method
tearDown() Will be called after each test-method
setUpClass() Will be called once per class (before the first test-method of this class)
tearDownClass() Will be called once per class (after the last test-method of this class)
Here's the official documentation
Here's a related answer
Change tearDownClass(self) to tearDownClass(cls) and setUpClass(self) to setUpClass(cls).
I am trying to mock the urllib2.urlopen library in a way that I should get different responses for different urls I pass into the function.
The way I am doing it in my test file now is like this
#patch(othermodule.urllib2.urlopen)
def mytest(self, mock_of_urllib2_urllopen):
a = Mock()
a.read.side_effect = ["response1", "response2"]
mock_of_urllib2_urlopen.return_value = a
othermodule.function_to_be_tested() #this is the function which uses urllib2.urlopen.read
I expect the the othermodule.function_to_be_tested to get the value "response1" on first call and "response2" on second call which is what side_effect will do
but the othermodule.function_to_be_tested() receives
<MagicMock name='urlopen().read()' id='216621051472'>
and not the actual response. Please suggest where I am going wrong or an easier way to do this.
The argument to patch needs to be a description of the location of the object, not the object itself. So your problem looks like it may just be that you need to stringify your argument to patch.
Just for completeness, though, here's a fully working example. First, our module under test:
# mod_a.py
import urllib2
def myfunc():
opened_url = urllib2.urlopen()
return opened_url.read()
Now, set up our test:
# test.py
from mock import patch, Mock
import mod_a
#patch('mod_a.urllib2.urlopen')
def mytest(mock_urlopen):
a = Mock()
a.read.side_effect = ['resp1', 'resp2']
mock_urlopen.return_value = a
res = mod_a.myfunc()
print res
assert res == 'resp1'
res = mod_a.myfunc()
print res
assert res == 'resp2'
mytest()
Running the test from the shell:
$ python test.py
resp1
resp2
Edit: Whoops, initially included the original mistake. (Was testing to verify how it was broken.) Code should be fixed now.