Mock object with pyTest

Question:

I have a function I need to test:

def get_user_by_username(db, username: str) -> Waiter:
    """Get user object based on given username."""
    user = db.query(Waiter).filter(Waiter.username == username).first()
    return user

Here I try to mock DB call and return correct Waiter object, so my test is:

def test_get_user_by_username(mocker):
    waiter = Waiter(id=10, username="string", password="1111")
    db_mock = mocker.Mock(return_value=waiter)
    result = get_user_by_username(db=db_mock, username="string")
    assert result.json()["username"] == "string"

But I get an error TypeError: 'Mock' object is not subscriptable. How I can do it?

According to @Don Kirkby answer, the right solution is:

def test_get_user_by_username(mocker):
    waiter = Waiter(id=10, username="string", password="1111")
    filter_method = mocker.Mock()
    filter_method.first = mocker.Mock(return_value=waiter)
    query = mocker.Mock()
    query.filter = mocker.Mock(return_value=filter_method)
    db_mock = mocker.Mock()
    db_mock.query = mocker.Mock(return_value=query)
    db_mock.query.filter.first = mocker.Mock(return_value=waiter)
    result = get_user_by_username(db=db_mock, username="string")
    assert vars(result)["username"] == "string"
Asked By: Vitalii Mytenko

||

Answers:

You’re calling several methods, so you need to mock all of them:

  • query()
  • filter()
  • first()

Here’s my best guess at how to do that with your code:

def test_get_user_by_username():
    waiter = Waiter(id=10, username="string", password="1111")
    filter_method = mocker.Mock()
    filter_method.first = mocker.Mock(return_value=waiter)
    query = mocker.Mock()
    query.filter = mocker.Mock(return_value=filter_method)
    db_mock = mocker.Mock()
    db_mock.query = mocker.Mock(return_value=query)
    db_mock.query.filter.first = mocker.Mock(return_value=waiter)
    result = get_user_by_username(db=db_mock, username="string")
    assert result.json()["username"] == "string"

I’m not sure what database library you’re using, but I recommend looking for a library that provides mock versions of the database methods. For example, I contributed a few features to django-mock-queries that lets you mock out the Django ORM. Either that, or separate the logic you want to test from the database access code.

Answered By: Don Kirkby