web-dev-qa-db-ja.com

堅牢なデザインを持つ関数のリファクタリング

ここに簡単なアプリの例があります:

データベースに保存されている本のリストを取得するためのユーザーからの要求を処理するこのコードがあるとしましょう。

_from .handlers import all_books
from flask import jsonify

@apps.route('/show/all', methods=['GET'])
@jwt_required
def show_books():
    user_name = get_jwt_identity()['user_name']
    books = all_books(user_name=user_name)
    return jsonify(books), 200
_

そして_handlers.py_には私が持っています:

_def all_books(user_name):
        db = get_db('books')
        books = []
        for book in db.books.find({'read_by':user_name}):
            books.append(book)
        return books
_

単体テストを作成しているときに、get_db()内でall_books()を使用すると、メソッドの単体テストが難しくなります。これがいい方法だと思いました。

_from .handlers import all_books

@apps.route('/show/all', methods=['GET'])
@jwt_required
def show_books():
    user_name = get_jwt_identity()['user_name']
    db = get_db('books')
    collection = db.books
    all_books(collection=collection,user_name=user_name)

def all_books(collection,user_name):
        books = []
        for book in collection.find({'read_by':user_name}):
            books.append(book)
        return books
_

使用するのに適したデザインは何ですか?最初の例や2番目の例のように、すべてのコードで1つの場所で1つのことを実行するのが適切です。

私には、1つの場所にすべての関連ロジックがあるため、最初はより明確に見えます。しかし、2番目のケースでは、ユニットテストのために偽のコレクションに合格する方が簡単です。

2
anekix

このようにフィルターロジックからdbクエリを分割すると...

def all_books(user_name):
        db = get_db('books')
        return filter_books_by_user(db.books,user_name)

def filter_books_by_user(collection,user_name):
        books = []
        for book in collection.find({'read_by':user_name}):
            books.append(book)
        return books

...両方を取得します-関数filter_books_by_userこれには「ロジック」が含まれていますが、データベースアクセスは含まれていません。また、関数all_booksフィルターロジックと共にdbクエリをカプセル化します。 filter_books_by_userは、DBなしでユニットテストできます。all_booksただし、おそらく統合テスト(DBを含む)によってより適切に検証されます。

3
Doc Brown

IO部分(データベースのクエリ、http要求、ローカルディスクの読み取り/書き込み操作など)があり、後処理ロジック(フィルタリング、並べ替え、検証)があります。テストする意味はほとんどありませんIO nitテスト:統合テストはこの目的のために設計されましたが、データベースなどを証明しますIOサブジェクトがそこにあり、期待どおりに応答します。ただし、純粋ロジックをIOから分離してはならないという意味ではありません。これは簡単にできることです。 (データベースがモックされている限り)テスト可能です。IOの部分が正常に機能すると想定して、データベースを分割し、純粋な部分をnitテストでカバーしてください。

1

すべてをユニットテストすることは理解できますが、それは良い考えではありません。 all_books関数には、ビジネス関連のロジックは含まれていません。これは、データベース内のすべての書籍を取得する100の方法の1つにすぎません。

これは統合テストでテストする必要があり、データベースを使用して書籍が適切に返されることを示しています。

この関数を単体テストすることもできますが、それは、注入するデータベースのモックオブジェクトを作成することを意味し、for inのテストだけが残ります。そして、将来的にデータベースを変更する予定がない場合(これは決して起こりません)は、効果がなく価値がありません。

0