参加する代わりに
Unittestは、おそらくPythonで最も有名なテスト作成フレームワークです。 プロジェクトで簡単に習得でき、使い始めることができます。 しかし、完璧なものはありません。 この投稿では、私が個人的に(1つではなく、私が)ユニットテストに欠けている1つの機能についてお話したいと思います。
ユニットテストについて少し
テストフレームワークについて議論(および非難)する前に、テスト全般について少し話す必要があると思います。 「ユニットテスト」という言葉を初めて聞いたとき、ソフトウェアモジュールが要件に準拠しているかどうかをチェックするのは、品質管理サービスの責任だと思いました。 これらの同じテストがプログラマーによって書かれるべきだとわかったとき、私の驚きを想像してください。 最初はテストを作成しませんでした...まったく。 朝起きて、ユーザーから「プログラムは動作しません。 急を要することがあります。」 最初は、エラーを発見した最初の単体テストを作成するまで、これは完全に正常なプロセスであるように思われました。 ユニットテストは一般に、問題を検出するまでまったく役に立たないようです。 今では、テストを書かずに新しい関数をコミットすることはできません。
ただし、テストは、コードの正しいスペルを検証するだけではありません。 私の意見では、テストを実行する関数のリストは次のとおりです。
- プログラムコードのエラーの検出
- プログラマーにコードが機能するという自信を与える
- 前の段落の結果:プログラムを安全に変更する能力
- テスト-システムの動作を最も正確に説明する一種のドキュメント
ある意味では、テストはプログラムの構造を繰り返します。 プログラム構築の原則はテストに適用可能ですか? はい-これは同じプログラムですが、別のプログラムをテストしています。
問題の説明
ある時点で、抽象的なテストを書くというアイデアを得ました。 それはどういう意味ですか? これは、それ自体は実行されませんが、相続人で定義されたパラメーターに依存するメソッドを宣言するテストです。 そして、私はユニットテストでこれを人間的に行うことができないことを発見しました。 以下に例を示します。
class SerializerChecker(TestCase): model = None serializer = None def test_fields_creation(self): props = TestObjectFactory.get_properties_for_model(self.model) obj = TestObjectFactory.create_test_object_for_model(self.model) serialized = self.serializer(obj) self.check_dict(serialized.data, props)
TestObjectFactoryとcheck_dictメソッドの実装を知らなくても、propsはオブジェクトのプロパティのディクショナリであり、objはシリアライザをチェックするオブジェクトであることは明らかです。 check_dictは、一致する辞書を再帰的にチェックします。 unittestに精通している多くの人が、このテストは抽象の私の定義を満たさないとすぐに言うと思います。 なんで? test_fields_creationメソッドはこのクラスから実行されるため、絶対に必要ではありません。 情報を検索したところ、TestCaseからSerializerCheckerを継承するのではなく、何らかの方法で相続人を実装することが最も適切なオプションであるという結論に達しました。
class VehicleSerializerTest(SerializerChecker, RecursiveTestCase): model = Vehicle serializer = VehicleSerialize
RecursiveTestCaseは、check_dictメソッドを実装するTestCaseの子孫です。
このソリューションは、いくつかの位置から一度にいです:
- SerializerCheckerクラスでは、子がTestCaseを継承する必要があることを知る必要があります。 この依存関係は、このコードに不慣れな人に問題を引き起こす可能性があります。
- SerializerCheckerにはcheck_dictメソッドがないため、開発環境は私が間違っていると固く信じています
開発環境がスローするエラー
check_dictのスタブを追加するだけで、すべての問題が解決されたように見えるかもしれません。
class SerializerChecker: model = None serializer = None def check_dict(self, data, props): raise NotImplementedError def test_fields_creation(self): props = TestObjectFactory.get_properties_for_model(self.model) obj = TestObjectFactory.create_test_object_for_model(self.model) serialized = self.serializer(obj) self.check_dict(serialized.data, props)
しかし、これは問題の完全な解決策ではありません。
- 実際、このクラスの子孫ではなく、アーキテクチャに妥当な質問を作成するRecursiveTestCaseを実装するインターフェイスを作成しました。
- TestCaseには多くのassert *メソッドがあります。 使用するたびにスタブを作成する必要が本当にありますか? それでも良い解決策のように思えますか?
まとめると
Unittestには、TestCaseから継承したクラスを「切断」するための健全な機能はありません。 このような機能がフレームワークに追加されたら、とてもうれしいです。 この問題をどのように解決しますか?