読者です 読者をやめる 読者になる 読者になる

Kesin's diary

プログラミングの記事がメインです

PythonでMongoDBを使うunittestのひな形

Python 開発

タイトルのとおりですが、ケースとしてはFlaskみたいな軽いフレームワークでMongoDBをデータベースに使うModelを自分で実装するような場合です。
実際にデータベースを動かすModelのテストを書くときに、FlaskはフルスタックのDjangoと違って何も面倒を見てくれないので、自分でテストのときだけ使用するdbの作成、破棄などを行う必要があります。

やりたいことは、

  • テスト起動時にテスト用のdb('test')を作成
  • テスト用のデータ(json)を予めdbに入れておく
  • Modelクラスのメソッドをテスト
  • テスト終了時にテスト用のdbの破棄

という流れです。mongodの起動と終了まで面倒を見てくれるともっと便利なはずですが、今回はそこまで求めなかったので、mongodは事前に起動しているという前提になります。 *1

コードにするとこんな感じです。Python2.7で動作確認しています。

#coding: utf-8
import unittest
import json
import pymongo

TEST_DATABASE = 'test'
TEST_COLLECTION = 'user'

class Model(object):
    """シンプルなModelのサンプル"""
    def __init__(self, host, port, db):
        self._client = pymongo.MongoClient(host, port)
        self._db = self._client[db]

    def insert_user(self, user_id, user_name):
        self._db.user.insert({'id': user_id, 'name': user_name})

    def find_user(self, user_id):
        return self._db.user.find_one({'id': user_id})

class TestMongoModel(unittest.TestCase):
    @classmethod
    def setUpClass(cls):
    """テストで使用するdbの用意"""
        cls.client = pymongo.MongoClient('localhost', 27017)
        cls.db = cls.client[TEST_DATABASE]

    @classmethod
    def tearDownClass(cls):
        """テストで使用したdbの破棄"""
        cls.client.drop_database(TEST_DATABASE)

    def setUp(self):
        """Modelインスタント取得、テスト用データの読み込み"""
        self.model = Model('localhost', 27017, TEST_DATABASE)

        with open('user.json', 'r') as fp:
            fixture = json.load(fp)
        self.db.user.insert(fixture)

    def tearDown(self):
        """テストケース終了後にコレクション破棄"""
        self.db.drop_collection(TEST_COLLECTION)

    def test_find_user(self):
         """Modelのfind_user()のテストケースサンプル"""
        self.model.insert_user(1, 'Alice')
        got = self.model.find_user(1)
        expect = 'Alice'
        self.assertEqual(got['name'], 'foo')

if __name__ == '__main__':
    unittest.main()

テスト用のdbの用意、破棄をそれぞれのテストケースで毎回行う必要はないので、setUpとtearDownのクラス版であるsetUpClass, tearDownClassで1度だけ行います。上のコードだと、'test'という名前のdbがテスト開始時に作られて、終了時に破棄されることになります。
あるテストケースの処理が前のテストケースでの処理(insert, delete, update)に依存しているとよくないので、setUpで各テストケースが実行される前にテストデータを読み込み、終了時にtearDownで毎回コレクションを破棄しています。

Mongo回りのセットアップが終われば、後はtest_find_user()のようにModelクラスのメソッドのテストケースを書いていくだけです。 今回はMongoDBの情報やdb、collection名を直接書いていますが、実際には設定ファイルや環境変数に追い出した方がアプリケーション本体からも使いまわせるので良いと思います。

ModelのテストがあるとControllerやViewを気にせずにロジック部分を作っていけるので、慣れないフレームワークで新しいDB(MongoDB)の練習をするときこそオススメです。

参考

*1:mongodまで面倒をみるなら、参考にあるブログや、test.mysqldが参考になりそうです