Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I mock sqlite3.Cursor

I've been pulling my hair out trying to figure out how to mock the sqlite3.Cursor class specifically the fetchall method.

Consider the following code sample

import sqlite3

from mock import Mock, patch
from nose.tools import assert_false


class Foo:
    def check_name(name):
        conn = sqlite3.connect('temp.db')
        c = conn.cursor()
        c.execute('SELECT * FROM foo where name = ?', name)
        if len(c.fetchall()) > 0:
            return True
        return False


@patch('sqlite3.Cursor.fetchall', Mock(return_value=['John', 'Bob']))
def test_foo():
    foo = Foo()
    assert_false(foo.check_name('Cane'))

Running nosetests results in no fun error

E
======================================================================
ERROR: temp.test_foo
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/koddsson/.virtualenvs/temp/lib/python2.7/site-packages/nose/case.py", line 197, in runTest
    self.test(*self.arg)
  File "/home/koddsson/.virtualenvs/temp/lib/python2.7/site-packages/mock.py", line 1214, in patched
    patching.__exit__(*exc_info)
  File "/home/koddsson/.virtualenvs/temp/lib/python2.7/site-packages/mock.py", line 1379, in __exit__
    setattr(self.target, self.attribute, self.temp_original)
TypeError: can't set attributes of built-in/extension type 'sqlite3.Cursor'

----------------------------------------------------------------------
Ran 1 test in 0.002s

FAILED (errors=1)

Should I not be able to mock the fetchall method or am I doing something horribly wrong?

like image 609
Kristjan Oddsson Avatar asked Dec 12 '13 06:12

Kristjan Oddsson


Video Answer


2 Answers

I would take the approach of patching out sqlite3 imported in your module and then work from there.

Let's assume your module is named what.py.

I would patch out what.sqlite3 and then mock the return value of .connect().cursor().fetchall.

Here is a more complete example:

from mock import patch
from nose.tools import assert_true, assert_false

from what import Foo

def test_existing_name():
    with patch('what.sqlite3') as mocksql:
        mocksql.connect().cursor().fetchall.return_value = ['John', 'Bob']
        foo = Foo()
        assert_true(foo.check_name('John'))
like image 73
Alex Couper Avatar answered Oct 14 '22 02:10

Alex Couper


I have found a way to mock sqlite3.Cursor in my tests:

cursor = MagicMock(Cursor)
cursor.fetchall.return_value = [{'column1': 'hello', 'column2': 'world'}]

I am pretty new in python but this is how I do it in Java.

like image 27
Piotr Ślesarew Avatar answered Oct 14 '22 03:10

Piotr Ślesarew