How can I patch sqlite3 cursor.execute in my unit test?

Viewed 161

I want to assert that sqlite3's execute method was called in a subclass.

Here's my module.py:

import sqlite3

class foo(object):
    def __init__(self):
        self.conn = sqlite3.connect("bla.db")
        self.cursor = self.conn.cursor()

    def table(self):
        self.cursor.execute("create table")

class foobar(foo):
    def __init__(self):
        super().__init__()

    def load(self):
        return self.table()

Here's my test file:

from module import foobar

@patch("module.sqlite3")
def test_foobar(mock_sqllite3):
    foobar().load()
    mock_sqllite3.connect().conn().cursor().execute().assert_called_once()

I'm getting an assertion error, but more to the point, I think i am way off in patching this properly

Is it possible to just mock the foo class attribute self.cursor?

1 Answers

Instead of using callable brackets () you should be using .return_value on each object that was called.

I have created two test functions; one simply mocks execute to facilitate a return value, and the other replaces the function altogether to achieve custom logic on the arguments.

import sqlite3
from unittest.mock import patch


class foo(object):
    def __init__(self):
        self.conn = sqlite3.connect("bla.db")
        self.cursor = self.conn.cursor()

    def table(self, p: str = None):
        return self.cursor.execute("create table")


class foobar(foo):
    def __init__(self):
        super().__init__()

    def load(self, p: str = None):
        return self.table(p)


@patch("__main__.sqlite3", spec=sqlite3)
def test_foobar(mock_sqllite3):
    mock_execute = (
        mock_sqllite3
            .connect
            .return_value
            .cursor
            .return_value
            .execute
    )

    mock_execute.return_value = "test"

    res = foobar().load()

    assert res == "test"
    mock_execute.assert_called_once()
    mock_execute.assert_called_with("create table")


@patch("__main__.sqlite3", spec=sqlite3)
def test_foobar_with_func_override(mock_sqllite3):
    mock_cursor = (
        mock_sqllite3
            .connect
            .return_value
            .cursor
    )

    mock_cursor.return_value.execute = lambda a: a.upper()

    res = foobar().load("test")

    assert res == "CREATE TABLE"

Let me know if any of this was not clear :)

Related