31

私はクラスを持っています:

class DatabaseThing():
     def __init__(self, dbName, user, password):
          self.connection = ibm_db_dbi.connect(dbName, user, password)

このクラスをテストしたいのですが、テスト データベースを使用します。だから私のテストクラスでは、次のようなことをしています:

import sqlite3 as lite
import unittest
from DatabaseThing import *

class DatabaseThingTestCase(unittest.TestCase):

    def setUp(self):
        self.connection = lite.connect(":memory:")
        self.cur = self.connection.cursor()
        self.cur.executescript ('''CREATE TABLE APPLE (VERSION INT, AMNT SMALLINT);
            INSERT INTO APPLE VALUES(16,0);
            INSERT INTO APPLE VALUES(17,5);
            INSERT INTO APPLE VALUES(18,1);
            INSERT INTO APPLE VALUES(19,15);
            INSERT INTO APPLE VALUES(20,20);
            INSERT INTO APPLE VALUES(21,25);''')

テストしたいクラスからの接続ではなく、この接続を使用するにはどうすればよいですか? setUp(self)からの接続の代わりにからの接続を使用することを意味しDatabaseThingます。クラスをインスタンス化せずに関数をテストすることはできません。テストクラスで何らかの方法でメソッドをモックしたいのですが、ドキュメント__init__に役立つと思われるものは何も見つかりませんでした。

4

6 に答える 6

52

モックする代わりに、単純にデータベース クラスをサブクラス化し、それに対してテストすることができます。

class TestingDatabaseThing(DatabaseThing):
     def __init__(self, connection):
          self.connection = connection

テストの代わりにそのクラスをインスタンス化します。DatabaseThingメソッドは同じであり、動作も同じですが、self.connection使用するすべてのメソッドは、代わりにテスト提供の接続を使用します。

于 2013-07-30T14:42:37.290 に答える
3

方法 1: サブクラス

@Martijn Pieters の回答を参照してください。

方法 2: コントロールの反転

長期的な解決策は、クライアントに接続を作成させ、それを渡してDatabaseThing使用させることです。DatabaseThing単一責任の原則を使用すると、この場合、接続を確立する責任を負うべきではないと思います。

この手法は依存関係を削減し、より多くの柔軟性を提供します。たとえば、接続プールをセットアップし、DatabaseThingプールから接続の新しいインスタンスをそれぞれ与えることができますDatabaseThing

于 2013-07-30T16:14:02.777 に答える
2

面倒で壊れやすくハックなinit関数を置き換えようとするのではなく、以下に示すように関数をデータベース コンストラクターに渡してみてください。

# Test connection creation
def connect_lite(dbName=None, user=None, password=None):
    connection = lite.connect(":memory:")
    cur = self.connection.cursor()
    cur.executescript ('''CREATE TABLE APPLE (VERSION INT, AMNT SMALLINT);
                          INSERT INTO APPLE VALUES(16,0);
                          INSERT INTO APPLE VALUES(17,5);
                          INSERT INTO APPLE VALUES(18,1);
                          INSERT INTO APPLE VALUES(19,15);
                          INSERT INTO APPLE VALUES(20,20);
                          INSERT INTO APPLE VALUES(21,25);''')
    return cur


# Production connection creation
def connect_ibm(dbName, user, password):
    return ibm_db_dbi.connect(dbName, user, password)

# Your DatabaseThing becomes:
class DatabaseThing():
    def __init__(self, connect, dbName, user, password):
        self.connection = connect(dbName, user, password)

# In your test create a DatabaseThing
t = DatabaseThing(connect_lite, dbName, user, password)

# In your production code create a DatabaseThing
p = DatabaseThing(connect_ibm, dbName, user, password)      

これには、使用しているデータベース テクノロジからコードをわずかに切り離すという副次的な利点があります。

于 2013-07-24T15:11:40.267 に答える
1

同じインターフェースを考慮ibm_db_dbiしてlite共有すると、これでうまくいくはずです:

import mock
import sqlite3 as lite

class DatabaseThingTestCase(unittest.TestCase):

    def setUp(self):
        self.patch = mock.patch('module_with_DatabaseThing_definition.ibm_db_dbi', lite)
        self.patch.start()

    def tearDown(self):
        self.patch.stop()

つまり、DatabaseThingファイルに名前database/things.pyを付けると、パッチは次のようになりますdatabase.things.ibm_db_dbi

モッキングの例:

moduleA.py

def connection(*args):
    print 'The original connection. My args', args

moduleB.py

def connection(*args):
    print 'The mocked connection. My args', args

myClass.py

import moduleA


class MyClass(object):
    def __init__(self):
        self.connection = moduleA.connection('Test', 'Connection')

test.py

import mock
import moduleB

from myClass import MyClass


def regular_call():
    MyClass()


def mocked_call():
    def wrapped_connection(*args):
        return moduleB.connection(':memory:')

    my_mock = mock.Mock(wraps=moduleB)
    my_mock.connection = wrapped_connection
    with mock.patch('myClass.moduleA', my_mock):
        MyClass()

    MyClass()

regular_call()
mocked_call()

test.pyを実行すると、次のようになります。

The original connection. My args ('Test', 'Connection')
The mocked connection. My args (':memory:',)
The original connection. My args ('Test', 'Connection')
于 2013-07-27T20:45:50.740 に答える