web-dev-qa-db-ja.com

Pythonユニットテスト:データベース操作を含むモジュールをユニットテストする方法は?

Pymysqlクライアントライブラリを使用して実際のデータベースに接続しています。モジュールに関数があり、pymysqlを使用してデータベースに接続し、データベースの挿入操作のみを実行します。実際のデータベースにアクセスせずに、この関数をpythonで単体テストするにはどうすればよいですか?

import pymysql

def connectDB(self):

# Connect to the database
connection = pymysql.connect(Host='localhost',
                             user='user',
                             password='passwd',
                             db='db')

try:
    with connection.cursor() as cursor:
        # Create a new record
        sql = "INSERT INTO `users` (`email`, `password`) VALUES (%s, %s)"
        cursor.execute(sql, ('[email protected]', 'newpassword'))


    connection.commit()

私のpythonバージョンは2.7です。

4
user2301

次のように、patchを使用できます。

from unittest.mock import patch, MagicMock

@patch('mypackage.mymodule.pymysql')
def test(self, mock_sql):
    self.assertIs(mypackage.mymodule.pymysql, mock_sql)

    conn = Mock()
    mock_sql.connect.return_value = conn

    cursor      = MagicMock()
    mock_result = MagicMock()

    cursor.__enter__.return_value = mock_result
    cursor.__exit___              = MagicMock()

    conn.cursor.return_value = cursor

    connectDB()

    mock_sql.connect.assert_called_with(Host='localhost',
                                        user='user',
                                        password='passwd',
                                        db='db')

    mock_result.execute.assert_called_with("sql request", ("user", "pass"))
8
uwevil

テストが重要である最も説得力のある理由の1つを再発見しました。それは、設計が悪いときに通知します。

別の言い方をすれば、テスト容易性はqualityの優れた1次プロキシです。次のことを考慮してください。

class DB(object):
    def __init__(self, **credentials):
        self._connect = partial(pymysql.connect, **credentials)

    def query(self, q_str, params):
        with self._connect as conn:
            with conn.cursor() as cur:
                cur.execute(q_str, params)
                return cur.fetchall()

# now for usage

test_credentials = {
    # use credentials to a fake database
}

test_db = DB(**test_credentials)
test_db.query(write_query, list_of_fake_params)
results = test_db.query(read_query)
assert results = what_the_results_should_be

複数のデータベースを使用する場合は、ポリモーフィズムを使用するか、APIの類似性に応じて、特定のDBをオブジェクトのコンストラクターパラメーターにすることができます。

1
Jared Smith

ハードコードされた値を返すスタブと呼ばれる一連の偽のデータベースが必要です。テスト中、これらのスタブは実際のデータベースの代わりに使用されます。私はPythonに精通していませんが、C++でこれを行う1つの方法は、データベースをコンストラクターパラメーターとして受け取るオブジェクトを作成することです。本番コードでは、実際のデータベースパラメータを使用し、テストではスタブを使用します。これは、コンストラクターが共通の基本クラスへのポインターを予期しているために実行できます。 Python Roy Osherove:ユニットテストの技術の最初の章を読むことをお勧めします。この本は、これらの偽のデータベースがスタブであり、モックではない理由を明確に説明しています。

0
robert