Unit testing conn() using mock:
app.py
import mysql.connector
import os, urlparse
def conn():
if "DATABASE_URL" in os.environ:
url = urlparse(os.environ["DATABASE_URL"])
g.db = mysql.connector.connect(
user=url.username,
password=url.password,
host=url.hostname,
database=url.path[1:],
)
else:
return "Error"
test.py
def test_conn(self):
with patch(app.mysql.connector) as mock_mysql:
with patch(app.os.environ) as mock_environ:
con()
mock_mysql.connect.assert_callled_with("credentials")
Error: Assertion mock_mysql.connect.assert_called_with is not called.
which I believe it is because 'Database_url' is not in my patched os.environ and because of that test call is not made to mysql_mock.connect.
Questions:
What changes do I need to make this test code work?
Do I also have to patch urlparse?
How do we mock in Python? Mocking in Python is done by using patch to hijack an API function or object creation call. When patch intercepts a call, it returns a MagicMock object by default. By setting properties on the MagicMock object, you can mock the API call to return any value you want or raise an Exception .
When you patch a function using mock, you have the option to specify autospec as True: If you set autospec=True then the mock with be created with a spec from the object being replaced. All attributes of the mock will also have the spec of the corresponding attribute of the object being replaced.
environ in Python is a mapping object that represents the user's environmental variables. It returns a dictionary having user's environmental variable as key and their values as value.
You can try unittest.mock.patch.dict solution. Just call conn with a dummy argument:
import mysql.connector
import os, urlparse
from unittest import mock
@mock.patch.dict(os.environ, {"DATABASE_URL": "mytemp"}, clear=True) # why need clear=True explained here https://stackoverflow.com/a/67477901/248616
def conn(mock_A):
print os.environ["mytemp"]
if "DATABASE_URL" in os.environ:
url = urlparse(os.environ["DATABASE_URL"])
g.db = mysql.connector.connect(
user=url.username,
password=url.password,
host=url.hostname,
database=url.path[1:],
)
else:
return "Error"
Or if you don't want to modify your original function try this solution:
import os
from unittest import mock
def func():
print os.environ["mytemp"]
def test_func():
k = mock.patch.dict(os.environ, {"mytemp": "mytemp"})
k.start()
func()
k.stop()
test_func()
For this, I find that pytest's monkeypatch fixture leads to better code when you need to set environment variables:
def test_conn(monkeypatch):
monkeypatch.setenv('DATABASE_URL', '<URL WITH CREDENTIAL PARAMETERS>')
with patch(app.mysql.connector) as mock_mysql:
conn()
mock_mysql.connect.assert_called_with(<CREDENTIAL PARAMETERS>)
The accepted answer is correct. Here's a decorator @mockenv to do the same.
def mockenv(**envvars):
return mock.patch.dict(os.environ, envvars)
@mockenv(DATABASE_URL="foo", EMAIL="[email protected]")
def test_something():
assert os.getenv("DATABASE_URL") == "foo"
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With