心配を止めお、プロパティベヌスのテストを曞き始める方法

最近、より倚くの堎合、特定の魔法のツヌルぞの参照がありたす-プロパティに基づいたテスト英語の文献をグヌグル怜玢する必芁がある堎合は、プロパティベヌスのテスト。 このトピックに関する蚘事のほずんどは、それがいかにクヌルなアプロヌチであるかを説明しおいたす。次に、基本的な䟋で、特定のフレヌムワヌクを䜿甚しおそのようなテストを蚘述する方法を瀺したす。 さらに、驚くず熱狂的な読者は、これらすべおを実践しようずしたすが、プロパティはどういうわけか発明されおいないずいう事実に基づいおいたす。 そしお、残念ながらこれはしばしば降䌏したす。 この蚘事では、少し異なった優先順䜍付けを詊みたす。 それでも、倚かれ少なかれ具䜓的な䟋から始めお、それがどんな動物なのかを説明したす。 しかし、そのような蚘事の䟋はあたり䞀般的ではないず思いたす。 次に、このアプロヌチに関連する問題のいく぀かず、それらの解決方法を分析したす。 しかし、さらに-プロパティ、プロパティ、およびプロパティのみ、それらをプッシュできる䟋がありたす。 面癜い

3぀の短いテストでKey-Valueストレヌゞをテストする


それで、䜕らかの理由で、ある皮のキヌバリュヌストレヌゞを実装する必芁があるずしたしょう。 ハッシュテヌブルに基づく蟞曞でも、ツリヌに基づく蟞曞でも、メモリに完党に保存するこずも、ディスクを操䜜するこずもできたす-気にしたせん。 䞻なこずは、次のこずを可胜にするむンタヌフェヌスが必芁であるこずです。


叀兞的な䟋ベヌスのアプロヌチでは、兞型的なテストは次のようになりたす。

storage = Storage() storage['a'] = 42 assert len(storage) == 1 assert 'a' in storage assert storage['a'] == 42 

たたは

 storage = Storage() storage['a'] = 42 storage['b'] = 73 assert len(storage) == 2 assert 'a' in storage assert 'b' in storage assert storage['a'] == 42 assert storage['b'] == 73 

そしお䞀般に、そのようなテストはdofigaよりも少し倚く曞くこずができ、たた曞く必芁がありたす。 さらに、内郚実装が難しいほど、ずにかく䜕かを芋逃す可胜性が高くなりたす。 芁するに、長く、退屈で、しばしば感謝のない仕事です。 それを誰かに抌し付けるのはどんなに玠晎らしいこずでしょう たずえば、コンピュヌタヌにテストケヌスを生成させたす。 たず、次のようなこずを詊しおください。

 storage = Storage() key = arbitrary_key() value = arbitrary_value() storage[key] = value assert len(storage) == 1 assert key in storage assert storage[key] == value 

これは最初のプロパティベヌスのテストです。 これは、埓来のものずほずんど同じように芋えたすが、小さなボヌナスはすでに印象的です-倩井から倀が取られおいないため、代わりに任意のキヌず倀を返す関数を䜿甚したす。 別のはるかに深刻な利点がありたす-䜕床も䜕床もさたざたな入力デヌタで実行しお、空のストレヌゞに芁玠を远加しようずするず実際に远加される契玄をチェックできたす。 さお、それはすべおうたくいっおいたすが、これたでのずころ、埓来のアプロヌチず比范しおあたり有甚ではありたせん。 別のテストを远加しおみたしょう。

 storage = arbitrary_storage() storage_copy = storage.copy() assert len(storage) == len(storage_copy) assert all(storage_copy[key] == storage[key] for key in storage) assert all(storage[key] == storage_copy[key] for key in storage_copy) 

ここでは、空のストレヌゞを䜿甚する代わりに、いく぀かのデヌタを䜿甚しお任意を生成し、そのコピヌが元のコピヌず同䞀であるこずを確認したす。 はい、ゞェネレヌタヌは朜圚的にバグのあるパブリックAPIを䜿甚しお䜜成する必芁がありたすが、原則ずしおこれはそれほど難しいタスクではありたせん。 同時に、実装に重倧なバグがある堎合、生成プロセス䞭にフォヌルが開始される可胜性が高いため、これは䞀皮のボヌナススモヌクテストず芋なすこずもできたす。 しかし今、私たちは確信するこずができたす-ゞェネレヌタヌが提䟛するこずができたすべおが正しくコピヌされたす。 そしお、最初のテストのおかげで、ゞェネレヌタヌが少なくずも1぀の芁玠でストレヌゞを䜜成できるこずを確信しおいたす。 次のテストの時間です 同時に、ゞェネレヌタヌを再利甚したす。

 storage = arbitrary_storage() backup = storage.copy() key = arbitrary_key() value = arbitrary_value() if key in storage: return storage[key] = value assert len(storage) == len(backup) + 1 assert key in storage assert storage[key] == value assert all(storage[key] == backup[key] for key in backup) 

任意のストレヌゞを取埗し、そこに別の芁玠を远加できるこずを確認したす。 そのため、ゞェネレヌタは2぀の芁玠を持぀リポゞトリを䜜成できたす。 たた、芁玠を远加するこずもできたす。 など数孊的垰玍法のようなこずをすぐに思い出したす。 その結果、䜜成された3぀のテストずゞェネレヌタヌにより、任意の数の異なる芁玠をリポゞトリに远加できるこずを確実に怜蚌できたす。 短いテストは3぀だけです これは基本的に、プロパティベヌスのテストの党䜓的な考え方です。


ずころで、このアプロヌチはTDDの原則ず矛盟したせん-テストはコヌドの前に同じ方法で曞くこずができたす少なくずも個人的には、私は通垞これを行いたす。 もう1぀のこずは、このようなテストをグリヌンにするこずは、埓来のテストよりもはるかに難しいこずですが、最終的に成功するず、コヌドが契玄の特定の郚分に実際に準拠するこずを確認したす。

これはすべお順調ですが、...


プロパティベヌスのテストアプロヌチのすべおの魅力により、倚くの問題がありたす。 この郚分では、最も䞀般的なものを芋぀けようずしたす。 そしお、有甚なプロパティを芋぀けるこずの実際の耇雑さに関する問題次のセクションで説明したすは別ずしお、初心者の最倧の問題は、倚くの堎合、良いカバレッゞに察する誀った自信です。 実際、䜕癟ものテストケヌスを生成するいく぀かのテストを䜜成したしたが、䜕が間違っおいるのでしょうか 前の郚分の䟋を芋るず、実際には倚くのこずがありたす。 たず、曞かれたテストでは、 storage.copyがポむンタヌをコピヌするだけでなく、実際に「ディヌプ」コピヌを䜜成するずいう保蚌はありたせん。 別のホヌル-探しおいるキヌがストアにない堎合、 ストレヌゞのキヌがFalseを返すずいう通垞の怜蚌はありたせん。 そしおリストは続きたす。 さお、私のお気に入りの䟋の1぀です。゜ヌトを䜜成したずしたしょう。䜕らかの理由で、芁玠の順序をチェックする1぀のテストで十分だず思いたす。

 input = arbitrary_list() output = sort(input) assert all(a <= b for a, b in zip(output, output[1:])) 

そしお、そのような実装は完党に合栌したす

 def sort(input): return [1, 2, 3] 

ここでのモラルが明確であるこずを願っおいたす。

ある意味で前の2぀の結果ず呌ぶこずができる次の問題は、プロパティベヌスのテストを䜿甚するこずは、真に完党なカバレッゞを達成するのが非垞に難しいこずが倚いずいうこずです。 しかし、私の意芋では、これは非垞に簡単に解決されたす-プロパティに基づいたテストだけを曞く必芁はなく、誰も埓来のテストをキャンセルしたせんでした。 さらに、人々は、具䜓的な䟋で物事を理解するのがはるかに簡単になるように配眮されおおり、䞡方のアプロヌチを䜿甚するこずを支持しおいたす。 䞀般的に、私は次のアルゎリズムをほが開発したした。非垞に単玔な埓来のテストを蚘述し、理想的にはAPIの䜿甚方法の䟋ずしお圹立぀ようにしたす。 「文曞化のための」テストで十分であるず感じたらすぐに、完党なカバレッゞからはほど遠い-プロパティに基づいたテストの远加を開始したす。

フレヌムワヌクの問題、それらに䜕を期埅するのか、なぜそれらが必芁なのかに぀いおです。結局のずころ、誰もあなたの手でサむクルをテストするこずを犁じたせん。 実際、喜びはテストの最初の秋たでであり、CIでではなく、ロヌカルである堎合は良いこずです。 たず、プロパティベヌスのテストはランダム化されおいるため、ドロップされたケヌスを確実に再珟する方法が必芁です。たた、自尊心のあるフレヌムワヌクでこれを行うこずができたす。 最も䞀般的なアプロヌチは、特定のシヌドをコン゜ヌルに出力するこずです。テストランナヌで手動でパヌムオフし、ドロップケヌスを確実に再生デバッグに䟿利するか、テスト開始時に最初に自動的にチェックされる「䞍良」sidでディスクにキャッシュを䜜成したす CIの再珟性に圹立ちたす。 もう1぀の重芁な偎面は、デヌタの瞮小倖郚゜ヌスの瞮小です。 デヌタはランダムに生成されるため、぀たり、1000芁玠のコンテナを䜿甚した萜䞋テストケヌスに完党に停のチャンスを圓おるこずができたすが、これは䟝然ずしおデバッグの「喜び」です。 したがっお、feylyaschyケヌスを怜出した埌の優れたフレヌムワヌクは、よりコンパクトな入力デヌタのセットを怜出しようずする倚くのヒュヌリスティックを適甚したすが、それでもテストはクラッシュし続けたす。 最埌に-倚くの堎合、テスト機胜の半分は入力デヌタゞェネレヌタヌであるため、単玔なゞェネレヌタヌからより耇雑なものをすばやく構築できる組み蟌みゞェネレヌタヌずプリミティブの存圚も非垞に圹立ちたす。

たた、プロパティに基づく論理テストが倚すぎるずいう批刀も時折ありたす。 ただし、これには通垞、次のスタむルの䟋が䌎いたす。

 data = totally_arbitrary_data() perform_actions(sut, data) if is_category_a(data): assert property_a_holds(sut) else if is is_category_b(data): assert property_b_holds(sut) 

実際、初心者向けのアンチパタヌンは非垞に䞀般的です。これをしないでください そのようなテストを2぀の異なるテストに分割し、それらに到達する可胜性が小さい堎合は䞍適切な入力デヌタ倚くのフレヌムワヌクではこのための特別なツヌルもありたすをスキップするか、すぐに適切なデヌタのみを生成するより特殊なゞェネレヌタヌを䜿甚するこずをお勧めしたす。 結果は次のようになりたす

 data = totally_arbitrary_data() assume(is_category_a(data)) perform_actions(sut, data) assert property_a_holds(sut) 

そしお

 data = data_from_category_b() perform_actions(sut, data) assert property_b_holds(sut) 

有甚な特性ずその生息地


さお、プロパティに基づいおテストするのに䜕が䟿利ですか、䞻な萜ずし穎が敎理されおいるこずは明らかです...いいえ、䞻なこずはただ明確ではありたせん-これらの同じプロパティをどこから取埗するのですか 怜玢しおみたしょう。

少なくずも萜ちないで


最も簡単なオプションは、テスト䞭のシステムに任意のデヌタを抌し蟌み、クラッシュしないこずを確認するこずです。 実際、これはファッショナブルな名前ファゞングを備えたたったく別の方向です。専甚ツヌルたずえば、AFL別名American Fuzzy Lopがありたすが、ある皋床拡匵するず、プロパティに基づいたテストの特別なケヌスず芋なすこずができたす。登っおいない堎合は、それから始めるこずができたす。 それにもかかわらず、原則ずしお、他のプロパティをチェックするずきに朜圚的な䜎䞋が通垞非垞によく出るので、明瀺的にそのようなテストはほずんど意味がありたせん。 この「プロパティ」に蚀及する䞻な理由は、読者をファザヌ、特にAFLこのトピックに関する英語の蚘事がたくさんありたすに導き、画像を完成させるこずです。

テストオラクル


最も退屈なプロパティの1぀ですが、実際には非垞に匷力なもので、芋かけよりもはるかに頻繁に䜿甚できたす。 アむデアは、同じこずを行うが異なる方法で実行される2぀のコヌドがある堎合があるずいうこずです。 そしお、特に、任意の入力デヌタを生成するこずを理解できず、䞡方のオプションでそれらを突き出し、結果が䞀臎するこずを確認できたす。 最も頻繁に匕甚されるアプリケヌションの䟋は、最適化されたバヌゞョンの関数を蚘述しお、䜎速だが単玔なオプションを残し、それに察しおテストを実行する堎合です。

 input = arbitrary_list() assert quick_sort(input) == bubble_sort(input) 

ただし、このプロパティの適甚範囲はこれに限定されたせん。 たずえば、非垞に倚くの堎合、テストするシステムによっお実装される機胜は、すでに暙準蚀語ラむブラリでも実装されおいるもののスヌパヌセットであるこずがわかりたす。 特に、通垞、キヌ倀ストレヌゞツリヌ、ハッシュテヌブル、たたはマヌクルパトリシアツリヌなどの゚キゟチックなデヌタ構造に基づくメモリたたはディスクのほずんどの機胜は、暙準の暙準蟞曞でテストできたす。 あらゆる皮類のCRUDのテスト-そこにもありたす。

私が個人的に䜿甚した別の興味深いアプリケヌション-システムの数倀モデルを実装するずきに、特別なケヌスを分析的に蚈算し、シミュレヌション結果ず比范できたす。 この堎合、原則ずしお、完党に任意のデヌタを入力に抌し蟌もうずするず、正しい実装であっおも、数倀解法の限られた粟床したがっお、適甚可胜性のためにテストが䜎䞋し始めたすが、修埩の過皋で、生成された入力デヌタに制限を課すこずにより、これらの同じ制限知られるようになる。

芁件ず䞍倉条件


ここでの䞻な考え方は、倚くの堎合、芁件自䜓がプロパティずしお䜿いやすいように定匏化されるずいうこずです。 そのようなトピックに関するいく぀かの蚘事では、䞍倉条件が個別に匷調されおいたすが、私の意芋では、これらの䞍倉条件のほずんどは芁件の盎接の結果であるため、ここの境界は䞍安定です。

プロパティのチェックに適したさたざたな分野の䟋の小さなリスト


原則ずしお、ここではすべおが完了しおいる、蚘事が完了しおいる、テストオラクルを䜿甚する、たたは芁件内のプロパティを探すず蚀うこずができたすが、別に興味深い「特殊なケヌス」がいく぀かありたす。

誘導ず状態のテスト


堎合によっおは、状態で䜕かをテストする必芁がありたす。 この堎合、最も簡単な方法


数孊的垰玍法に非垞に䌌おいたす


別の方法砎損した堎所に぀いおもう少し情報を提䟛するこずもありたすは、蚱容可胜なむベントシヌケンスを生成し、テスト䞭のシステムに適甚しお、各ステップの埌にプロパティをチェックするこずです。

前埌に


突然、いく぀かのデヌタの盎接および逆倉換のためにいく぀かの関数をテストする必芁があった堎合、非垞に幞運だず考えおください

 input = arbitrary_data() assert decode(encode(input)) == input 

テストに最適


特別ですが、興味深いケヌスは反転です

 input = arbitrary_data() assert invert(invert(input)) == input 

顕著な䟋は、マトリックスの反転たたは転眮です。

べき等


䞀郚の操䜜は、繰り返し䜿甚した結果を倉曎したせん。 兞型的な䟋


通垞のデコヌド゚ンコヌド入力==入力メ゜ッドが、同等の入力デヌタの衚珟が異なるために適切でない堎合、due等性を䜿甚しおシリアル化-逆シリアル化をテストするこずもできたすJSONの䜙分なスペヌス

 def normalize(input): return decode(encode(input)) input = arbitrary_data() assert normalize(normalize(input)) == normalize(input) 

さたざたな方法、1぀の結果


ここでのアむデアは、同じこずを行うための方法がいく぀かあるこずがあるずいう事実を利甚するこずに芁玄されたす。 これはテストオラクルの特殊なケヌスのように芋えるかもしれたせんが、実際にはそうではありたせん。 最も簡単な䟋は、いく぀かの操䜜の可換性を䜿甚しおいたす。

 a = arbitrary_value() b = arbitrary_value() assert a + b == b + a 

些现なこずのように思えるかもしれたせんが、これはテストするのに最適な方法です。


さらに、蟞曞ぞの芁玠の远加には同じプロパティがありたす。

 A = dict() A[key_a] = value_a A[key_b] = value_b B = dict() B[key_b] = value_b B[key_a] = value_a assert A == B 

このオプションはもっず耇雑です-私は長い間、蚀葉でそれを説明する方法を考えおいたしたが、数孊的衚蚘だけが思い浮かびたす。 䞀般に、このような倉換は䞀般的です fxプロパティが保持するもの fx+y=fx cdotfy、および匕数ず関数の結果の䞡方が必ずしも単なる数字ではなく、挔算 +そしお  cdot-これらのオブゞェクトに察するいく぀かのバむナリ操䜜。 これで䜕をテストできたすか


もう少し「通垞の」タスクの䟋-いく぀かのトリッキヌな蟞曞マヌゞアルゎリズムをテストするには、次のようなこずができたす。

 a = arbitrary_list_of_kv_pairs() b = arbitrary_list_of_kv_pairs() result = as_dict(a) result.merge(as_dict(b)) assert result == as_dict(a + b) 

結論の代わりに


基本的に、この蚘事で䌝えたかったこずはこれだけです。 それがおもしろくお、もう少し倚くの人がこのすべおを実践し始めるこずを願っおいたす。 タスクを少し簡単にするために、蚀語ごずに有効性の皋床が異なるフレヌムワヌクのリストを瀺したす。


そしおもちろん、か぀お玠晎らしい蚘事を曞いおくれた人々に特別な感謝をしたす。数幎前、私はこのアプロヌチに぀いお孊び、心配をやめお、プロパティに基づいたテストを曞き始めたした。

Source: https://habr.com/ru/post/J434008/


All Articles