I am trying to mock a bigtable call in my unit test by declaring fixtures like so:
#pytest.fixture()
def bigtableMock():
bigtableMock = Mock(spec=google.cloud.bigtable.table.Table)
yield bigtableMock
#pytest.fixture()
def bigtableInstanceMock(bigtableMock):
bigtableInstanceMock = Mock(spec=google.cloud.bigtable.instance.Instance)
bigtableInstanceMockAttrs = {'table': bigtableMock}
bigtableInstanceMock.configure_mock(**bigtableInstanceMockAttrs)
yield bigtableInstanceMock
#pytest.fixture()
def myDao(bigtableInstanceMock):
yield MyDao(bigtableInstanceMock)
I mock the read_rows function like so:
def mockReadRowsFuncWith1Dto(testDto):
mockTableRowData = {}
mockTableRowData['columnFamily'] = asDict(testDto)
rowDataMock = MagicMock()
rowDataMock.__iter__.return_value = [mockTableRowData]
rowDataMock.__len__ = 1
def mockReadRowsFunc(startKey, endKey, limit, end_inclusive):
return rowDataMock
return mockReadRowsFunc
When I call my test function:
def test_read_table(
myDao,
testDto,
bigtableMock
):
bigtableMock.read_rows = mockReadRowsFuncWith1Dto(testDto)
samp = bigtableMock.read_rows(
startKey="asdf",
endKey="sadf",
limit=1,
end_inclusive=True
)
print(f"\test data {samp}")
myDao.readTable(...)
Inside myDao.readTable I call read_rows like so:
tableRows: PartialRowData = self.table.read_rows(
start_key=startKey,
end_key=endKey,
limit=10,
end_inclusive=True
)
However, I do not get the magicMock return that I expect inside readTable, tableRows:<Mock name='mock.table().read_rows()' id='4378752480'>, whereas in the test function I can print out the Magic mock: test data <MagicMock id='4413191168'>. Regardless of the print statement or not, I can never invoke the correct mocked read_rows function. What am I doing wrong?
The problem in my case was that the fixture for bigtableMock was different between test_read_table and the fixture for myDao. I modified my test cases to include the table mocking inside the bigtableMock like so:
#pytest.fixture(read_row_data, mock_append_row)
def bigtableMock():
bigtableMock = Mock(spec=google.cloud.bigtable.table.Table)
bigtableMock.read_rows.return_value = [read_row_data]
bigtableMock.append_row.return_value = mock_append_row
yield bigtableMock
Related
The script I want to test looks like this
class cnv_classifier:
´´´ A class that calls another classes´´´
def __init__(self,user_input):
# First class
data_loaded = ReadFile(user_input)
data_loaded = data.load()
# Second class
cnv_df = Filter(data_loaded)
cnv_df = cnv_df_cvf_return_cnv()
Now, I want to test the inner classes. To test the first class I have done this
pytest.fixture()
def file_to_test():
file_to_test = "path/file_name"
return file_to_test
def test_data_loaded(file_to_test):
data_loaded = ReadFile(user_input)
data_loaded = data.load()
assert ...
# This works
Now, to test the second class, I need the outpput of the first class. How can I do this?
I have tried introducing a return, and call the function like this
def test_data_loaded(file_to_test):
data_loaded = ReadFile(user_input)
data_loaded = data.load()
assert ...
return
# Call the function
test_data_loaded = test_data_loaded(file_to_test)
# Test the second class using the output of the first class
def test_filter():
cnv_df = Filter(data_loaded)
cnv_df = cnv_df_cvf_return_cnv()
assert ...
This does not work, when I run pytest, it says data_loaded is not found
What I am doing wrong?
You can do what you're trying to do in the end, and is perfectly acceptable. But the solution is a bit different.
To run a test inside another running test you should add all fixtures to the latter. In your case the code would look like this:
def test_data_loaded(file_to_test):
data_loaded = ReadFile(user_input)
data_loaded = data.load()
assert ...
return data_loaded # <- Here
# Test the second class using the output of the first class
def test_filter(file_to_test):
data_loaded = test_data_loaded(file_to_test) # <- Here
cnv_df = Filter(data_loaded)
cnv_df = cnv_df_cvf_return_cnv()
assert ...
I have sample code and test:
def outer():
inner_response = inner(param1)
def inner(something):
queryset_response = something.object.filter(foo="bar",
foo1="bar1") #this should get a reponse when testing.
response_list = []
for count, queryset_res in enumerate(queryset_response):
response_list.append(queryset_response[count].data)
#get response for data in this line.
return response_list
I wanna test this situation using mock and probably return list of queryset using mock if possible.
def setup():
something = mock.Mock()
def test_outer():
# what should be done to the below line work so that
# response_list.append gets some value.
something.objects.filter()[0].data = "some string"
# Also is it possible to return queryset as like shown below.
something.objects.filter().return_value = <queryset> # list of objects in queryset.
i used a mock and returned namedtuple to make it behave like queryset and use (.) dot to access the data set. I could make a class and used it same way.
def test_func():
something = mock.Mock()
key = collections.namedtuple('key', 'data')
response = key('some_string')
something.objects.filter.return_value = [response]
this is kinda mocking django as gloo said, my ex-engineers decided to opt in that way.
You shouldn't be mocking the result of a filter as that would be like unit testing django itself. Instead, you should be testing your own functions that will call Model.object.filter. You can create the objects you will be working with in the setup of your unit test, and assert that when you call your function, the expected result is the same as those objects. For example:
def my_own_function(foo, foo1):
queryset_response = Something.objects.filter(foo=foo, foo1=foo1)
store = queryset_response[0].data
return store
and in your unit test:
def test_my_own_function():
data = "mydata"
sample_obj = Something.objects.create(foo="bar", foo1="bar1", data=data)
result = my_own_function("bar", "bar1")
self.assertEqual(result, data)
According to the Pytest documentation, we can use the record_testsuite_property fixture to record properties specific to the test suite.
So I'm using that fixture like this:
import pytest
class TestSuite:
#pytest.fixture(scope="class")
def init(self, record_testsuite_property):
record_testsuite_property("suite_name", "Test Suite #1")
def test_example(self, record_property):
record_property('test_id', 'ABC-123')
record_property('test_name', 'Example Test #1')
assert True
I would like to access the value of suite_name when I am generating the report like so:
#pytest.mark.hookwrapper
def pytest_runtest_makereport(item, call):
outcome = yield
report = outcome.get_result()
if item.user_properties:
test_properties = { prop[0]: prop[1] for prop in item.user_properties }
# These are added via `record_property` fixture and I am able to access them with no issue.
report.test_id = test_properties["test_id"]
report.test_name = test_properties["test_name"]
# Not able to get the suite_name from here.
# report.suite_name = test_properties["suite_name"]
setattr(report, "duration_formatter", "%M:%S")
I was able to figure it out.
The whole idea is that I want to have suite_name be a property that is attached to each item so that I can include it in the report.
So I realized that I would still use the record_property fixture here and have it automatically requested (using autouse=True) at the function scope.
import pytest
class TestSuite:
#pytest.fixture(scope="function", autouse=True)
def init(self, record_property):
record_property("suite_name", "Test Suite #1")
def test_example(self, record_property):
record_property('test_id', 'ABC-123')
record_property('test_name', 'Example Test #1')
assert True
And now I can access suite_name here:
#pytest.mark.hookwrapper
def pytest_runtest_makereport(item, call):
outcome = yield
report = outcome.get_result()
if item.user_properties:
test_properties = { prop[0]: prop[1] for prop in item.user_properties }
report.test_id = test_properties["test_id"]
report.test_name = test_properties["test_name"]
report.suite_name = test_properties["suite_name"]
setattr(report, "duration_formatter", "%M:%S")
my project has a file called config.py which has, among others, the following code:
class Secret(Enum):
DATABASE_A = 'name_of_secret_database_A'
DATABASE_A = 'name_of_secret_database_A'
def secret(self):
if self.value:
return get_secret(self.value)
return {}
def get_secret(secret_name):
session = Session()
client = session.client(
service_name='secretsmanager',
region_name='us-east-1',
)
secret_value = client.get_secret_value(SecretId=secret_name)
return loads(secret_value.get('SecretString', "{}"))
I need to somehow mock get_secret in tests with pytest for all enum calls, for example Secret.DATABASE_A.secret ()
You can use monkeypatch to override the behaviour of get_secret(). I have made the get_secret() method a static method of the Secret class, but you can make it part of any module you want and import it as well. Just make sure you change in in the monkeypatch.setattr() call as well.
import pytest
from enum import Enum
class Secret(Enum):
DATABASE_A = 'name_of_secret_database_A'
DATABASE_B = 'name_of_secret_database_B'
def secret(self):
if self.value:
return Secret.get_secret(self.value)
return {}
#staticmethod
def get_secret(secret_name):
session = Session()
client = session.client(
service_name='secretsmanager',
region_name='us-east-1',
)
secret_value = client.get_secret_value(SecretId=secret_name)
return loads(secret_value.get('SecretString', "{}"))
def test_secret_method(monkeypatch):
def get_secret(secret_name):
return "supersecret"
monkeypatch.setattr(Secret, "get_secret", get_secret)
s = Secret.DATABASE_A
assert s.secret() == "supersecret"
This returns into 1 passed test.
What is happening here is, that I created a function get_secret() in my test_secret_method as well, and then overwrite the Secret.get_secret() with that new method. Now, you can use the Secret class in your test_method and be sure what the 'get_secret()' method will return without actually running the original code.
i am new to python unit testing. Want to mock a function that calls other functions.
Here is my function that i want to mock
def has_groups(self, group_names):
auth_user_id = AuthUser.get_by_email(self.userEmail).id
auth_user_groups = AuthUserGroups.get_group_by_user_id(auth_user_id)
for auth_user_group in auth_user_groups:
if auth_user_group.group.name in group_names:
return True
return False
has_groups should return True only when it get's 'Admin' as parameter.
Here is my test
def my_test(self):
uid = self.auth_user.get_by_email = Mock(return_value=73)
groups = AuthUserGroups.get_group_by_user_id = Mock(uid, return_value='Admin')
self.auth_user.has_groups = Mock(groups, return_value=True)
but it's not working fine. I will appreciate if anyone help me
Can i use patch decorator for this and how?
As I understand your has_groups function is method. I think it's better to mock whole class or independent function. On this situation you could mock AuthUserGroups, method return value and patch module with has_groups method implementation. So you'll have test like this:
from unittest import mock
def my_test(self):
group = mock.MagicMock()
group.group.name = 'Admin'
fake_auth_user_groups = mock.MagicMock()
fake_auth_user_groups.get_group_by_user_id.return_value = [group]
with mock.patch('your_module.AuthUserGroups', fake_auth_user_groups):
self.auth_user.has_groups(['Admin'])