Login

Db Mock

Author:
akhavr
Posted:
July 27, 2007
Language:
Python
Version:
.96
Score:
2 (after 2 ratings)

I hate when my unittest hits database. Especially when each test case needs different dataset.

So I wrote this db mock, that's local to specific test and uses sqlite3 in-memory db.

Usage (nosetests):

class TestMainNoData(DbMock):
    'testing main function with no meaningful data'

    def test_no_posts(self):
        'there are no posts'
        assert models.Post.objects.count() == 0, 'should be no feeds'
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
# -*- coding: utf-8 -*-

'''
# $Id: django_db_mockup.py 375 2007-07-25 10:45:14Z akhavr $
'''

import copy
import django.conf
from django.core.management import syncdb
from django.db import backend, connection
import django.db.backends.sqlite3.base as django_sqlite
from django.db.backends import util
from pysqlite2 import dbapi2 as sqlite
    
def temp_django_db_conn():
    'create temporary django db connection'
    sqlite_conn = django_sqlite.DatabaseWrapper()
    sqlite_conn.connection = sqlite.connect(
        database=':memory:',
        detect_types=sqlite.PARSE_DECLTYPES | sqlite.PARSE_COLNAMES)
    # define some functions that are missing from sqlite
    sqlite_conn.connection.create_function('concat', 2,
                                           lambda *args: ''.join(args))
    return sqlite_conn

def subst_django_db_conn(new_conn):
    '''
    substitute django db connection by <new_conn>
    warning: black magic involved
    '''
    # black magic ;)
    klass = connection.__class__
    orig_conn = klass.__new__(klass)
    orig_conn.__dict__.update(connection.__dict__)

    connection.__class__ = new_conn.__class__
    connection.__dict__.update(new_conn.__dict__)
    return orig_conn

class DbMock:
    def setup(self):
        'db mockup setup'
        self.sqlite_conn = temp_django_db_conn()
        self.orig_conn = subst_django_db_conn(self.sqlite_conn)

        self.orig_db_engine = django.conf.settings.DATABASE_ENGINE
        django.conf.settings.DATABASE_ENGINE = 'sqlite3'
    
        self.orig_db_name = django.conf.settings.DATABASE_NAME
        django.conf.settings.DATABASE_NAME = ':memory:'
    
        self.supports_constraints = backend.supports_constraints
        backend.supports_constraints = False

        self.operator_mapping = copy.deepcopy(backend.OPERATOR_MAPPING)
        backend.OPERATOR_MAPPING.update(django_sqlite.OPERATOR_MAPPING)
    
        syncdb(verbosity=0, interactive=False)
        return

    def teardown(self):
        'db mockup teardown'
        subst_django_db_conn(self.orig_conn)
        django.conf.settings.DATABASE_ENGINE = self.orig_db_engine
        django.conf.settings.DATABASE_NAME = self.orig_db_name
        backend.supports_constraints = self.supports_constraints
        backend.OPERATOR_MAPPING = self.operator_mapping
        return

More like this

  1. Template tag - list punctuation for a list of items by shapiromatron 10 months, 2 weeks ago
  2. JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 10 months, 3 weeks ago
  3. Serializer factory with Django Rest Framework by julio 1 year, 5 months ago
  4. Image compression before saving the new model / work with JPG, PNG by Schleidens 1 year, 6 months ago
  5. Help text hyperlinks by sa2812 1 year, 6 months ago

Comments

akhavr (on July 27, 2007):

Several reasons:

  • django's built-in testing framework doesn't use in-memory db for unit tests (unless it is configured for sqlite)
  • I use not doctests, nor unittest but nose for my unit testing and nose+twill, which do not have ready-to-use plug into django testing framework
  • in particular it's not clear how django+nose can load fixtures, defined in test functions and test classes
  • inheriting from DbMock switches db context only temporary and I can run some tests against in-memory sqlite and some - against mysql used in production

I've not been into django testing framework because I use django from 0.91 era when it had none (and wasn't always following django-users :) So I'll look deeper into django's testing framework to see how it could be twisted to do what I need.

#

Please login first before commenting.