tanihito’s blog

デジタル・新規事業開発・健康など、興味のあることについてつらつらと書いてきます。

moxを使ってMySQLdbのテストを行う

ユニットテストを行う際に面倒なのが、外部のデータベースを利用している場合です。
ここではmoxというモックを使ってMySQLdbのテストを行なう方法を紹介します。
moxpythonのモックライブラリの一種で、JavaのEasyMockを元にしています。
詳細はpymoxのページを見てもらうとして、ここではmoxを使った具体例を示します。


db1のtable1に対して「SELECT * FROM table1」というSQLを実行するプログラム
mymodel.pyを考えます。例えばtable1が

key value
key1 value1
keu2 value2

という構造になっている場合、下記のkey, valueペアを返します。

(('key1', 'value1'), ('key2', 'value2'))


ここで外部のデータベースが行うことは、

  • 「SELECT * FROM table1」というSQLを受け取る
  • key, valueのペアを返す

の2つです。test_mymodel.pyの

self.cursor.execute("SELECT * FROM table1")
self.cursor.fetchall().AndReturn((('key1', 'value1'), ('key2', 'value2')),)

がこの2つの機能を実装しています。


mymodel.py

# -*- coding: utf-8 -*-
import MySQLdb

def select():
    con = MySQLdb.connect(host = '127.0.0.1',
                          db = 'db1',
                          user = 'user1',
                          passwd = 'password1',)
    cursor = con.cursor()
    
    query = "SELECT * FROM table1"
    cursor.execute(query)
    res = cursor.fetchall()
    return res

if __name__ == "__main__":
    select()

test_mymodel.py

# -*- coding: utf-8 -*-
import mymodel
import MySQLdb
import MySQLdb.cursors
import mox

class Test():
    def setUp(self):
        self.mox = mox.Mox()
        self.cursor = self.mox.CreateMock(MySQLdb.cursors.Cursor)
        self.con = self.mox.CreateMockAnything()

    def tearDown(self):
        self.mox.UnsetStubs()

    def test_select(self):
        self.mox.StubOutWithMock(MySQLdb, 'connect')
        MySQLdb.connect(host = mox.IsA(str), db = mox.IsA(str),
                        user = mox.IsA(str), passwd = mox.IsA(str),).AndReturn(self.con)
        self.con.cursor().AndReturn(self.cursor)
        self.cursor.execute("SELECT * FROM table1")
        self.cursor.fetchall().AndReturn((('key1', 'value1'), ('key2', 'value2')),)

        self.mox.ReplayAll()
        res = mymodel.select()
        assert res == (('key1', 'value1'), ('key2', 'value2'))
        self.mox.VerifyAll()

if __name__ == "__main__":
    t = Test()
    t.test_select()