Analytics Vidhya
Published in

Analytics Vidhya

Pytest mocking cheatsheet

Ello, ello, ello my fellow engineers!

It’s been a while since I wrote one of these, but I’ve recently started a quest in sharing knowledge and maybe saving someone from banging their head against a wall the same way I did to solve a problem.

Here, I’ve put together some of my most commonly used pytest fixture mocks.

All these mocks I store in conftest.py and then just pull them into the tests I create as and when I need them.

Dependencies:

  • pytest
  • pytest_mock
  • flask
  • flask_sqlalchemy

How to Mock an Object:

"""conftest.py"""import pytest@pytest.fixture
def mock_object():
class MockObject(object):
object_id = "id"
some_property = "some_property_value"

def to_json(self):
return {
"objectId": self.object_id,
"someProperty": self.some_property,

}
return MockObject()

And we can use this in our tests like so:

"""test_functions.py"""def test_mocked_object(mock_object):
mock_object.object_id = "new_id"

assert mock_object.to_json()["objectId"] == "new_id"

Obviously you can make these as complex as you need to but for the sake of saving time I’ll keep it simple.

How to Mock an Object property:

"""conftest.py"""import pytest@pytest.fixture
def mock_object_property(mocker):
mock = mocker.patch(
"test_functions.MyObject.property",
new_callable=mocker.PropertyMock,
return_value="new_property_value"
)
return mock

If you use this in the following way you’ll notice that “some_property” has magically become “new_property_mock”:

"""test_functions.py"""

class MyObject(object):
property = "some_property"

def test_mocked_property(mock_object_property):
test_object = MyObject()
assert test_object.property == "new_property_value"

And now the most commonly used mock…

How to Mock a Function return:

"""conftest.py"""import pytest@pytest.fixture
def mock_function(mocker):
return mocker.patch("test_functions.my_function")

You can set the return value inside the conftest, but to make it more flexible I tend to set this inside the test:

"""test_functions.py"""

def my_function():
return "my_function_value"

def test_mock_function(mock_function):
mock_function.return_value = "new_value"
response = my_function()

assert response == "new_value"

How to Mock a SQLAlchemy database model:

Let’s say you have the following model in your function.py file

"""functions.py"""from flask_sqlalchemy import SQLAlchemy
from sqlalchemy.dialects.postgresql import VARCHAR
db = SQLAlchemy()class MyModel(db.Model):
id = db.Column(
"id",
VARCHAR,
primary_key=True,
unique=True,
nullable=False,
)

You can mock this like so:

"""conftest.py"""import pytest
from functions_to_mock.functions import MyModel

@pytest.fixture
def mock_my_model():
my_model = MyModel(
id="my_mock_id",
)
return my_model

And use in your test case:

"""test_functions.py"""def test_sqlalchemy_model_mock(mock_my_model):

my_model = mock_my_model

assert my_model.id == "my_mock_id"

The most common way I’ve utilised the above model mock is while mocking the SQLAlchemy query property get function (the main way I query databases).

Mock SQLAlchemy query property get function:

The below is how to mock the query property get function:

"""conftest.py"""import pytest@pytest.fixture
def mock_get_sqlalchemy(mocker):
mock = mocker.patch("flask_sqlalchemy._QueryProperty.__get__").return_value = mocker.Mock()
return mock

Re-using `MyModel` and the `mock_my_model` from earlier, we could utilise this in a test like so:

"""test_functions.py"""from functions_to_mock.functions import MyModel

def test_sqlalchemy_query_property_get_mock(
mock_my_model,
mock_get_sqlalchemy,
):
# SQLAlchemy returns a list for an "all" query
mock_get_sqlalchemy.all.return_value = [mock_my_model]
response = MyModel.query.all()

assert response == [mock_my_model]

Lovely jubbly!

How to Mock a Flask application:

Finally, quite a commonly used mock for those of us developing applications.

Let’s say you have the following application:

"""functions.py"""from flask import Flask, jsonify

app = Flask(__name__)

@app.route("/", methods=['GET'])
def get_value():
return jsonify("some_value"), 200

This is a very simple application example, but in the real world this would read in configurations for users, databases etc. So in most cases you would want to mock, so you don’t set up the entire application for unit tests.

"""conftest.py"""import pytest
from flask import Flask
@pytest.fixture
def flask_app_mock():
"""Flask application set up."""
app_mock = Flask(__name__)
return app_mock

Used in the following way:

"""test_functions.py"""from functions_to_mock.functions import get_value

def test_flask_app_light_mock(
flask_app_mock,
):
with flask_app_mock.app_context():
response = get_value()

assert response[0].json == "some_value"

Time for some “real world” complex application level mocking using mocks we created previously…

BONUS: Mock full system (master your mocking)

This is where things really heat up! Let’s say you have the following small flask application in a functions.py file. This contains an application that has a SQLAlchemy bind to a model (MyModel). With a home route, that returns the value of an id in our database.

"""functions.py"""from flask import Flask, jsonify
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy.dialects.postgresql import VARCHAR
db = SQLAlchemy()app = Flask(__name__)
app.config["SQLALCHEMY_DATABASE_URI"] = "postgresql+pg8000://alex@localhost:5432/test_alex"
app.config["SQLALCHEMY_BINDS"] = {
"test_alex": app.config["SQLALCHEMY_DATABASE_URI"]
}
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
db.init_app(app)
class MyModel(db.Model):
id = db.Column(
"id",
VARCHAR,
primary_key=True,
unique=True,
nullable=False,
)
@app.route("/", methods=['GET'])
def get_value():
mod = MyModel.query.first()
return jsonify(mod.id), 200

We can utilise what we have previously learnt and add the the following fixtures to conftest.py file to help mock the above system:

"""conftest.py"""import pytest

from flask import Flask
from flask_sqlalchemy import SQLAlchemy

from functions_to_mock.functions import MyModel
# This fixture will mock your DB table model
@pytest.fixture
def mock_my_model():
my_model = MyModel(
id="my_mock_id",
)
return my_model
# This fixture will mock the SQLAlchemy query response from the DB
@pytest.fixture
def mock_get_sqlalchemy(mocker):
mock = mocker.patch("flask_sqlalchemy._QueryProperty.__get__").return_value = mocker.Mock()
return mock
#This fixture will mock your flask application
@pytest.fixture
def flask_app_mock():
"""Flask application mock set up."""
app_mock = Flask(__name__)
db = SQLAlchemy(app_mock)
db.init_app(app_mock)
return app_mock

Your happy path unit test looks like this:

"""test_functions.py"""
from functions_to_mock.functions import get_value


def test_flask_app_mock(
flask_app_mock,
mock_my_model,
mock_get_sqlalchemy,
):
mock_get_sqlalchemy.first.return_value = mock_my_model
with flask_app_mock.app_context():
response = get_value()

assert response[0].json == "my_mock_id"

I hope you’ve found this useful. If you do I’ll look at creating some more cheatsheet type posts.

I have also started a knowledge sharing channel on YouTube if you are a more auditory learner: https://www.youtube.com/channel/UCEQ8k6Ybr3P-XqScPBMoikA

Happy mocking!

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store