契約ベースのプログラミングと単体テスト


13

私はやや防御的なプログラマーであり、Microsoft Code Contractsの大ファンです。

現在、私は常にC#を使用できるわけではなく、ほとんどの言語で私が持っている唯一のツールはアサーションです。だから私は通常、このようなコードになります:

class
{       
    function()
    {   
         checkInvariants();
         assert(/* requirement */);

         try
         {
             /* implementation */
         }
         catch(...)
         {
              assert(/* exceptional ensures */);                  
         }
         finally
         {
              assert(/* ensures */);
              checkInvariants();
         }
    }

    void checkInvariants()
    {
         assert(/* invariant */);
    }
}

ただし、このパラダイム(または、あなたがそれを呼ぶものは何でも)は、多くのコードを乱雑にします。

私はそれが本当に努力の価値があるかどうか、そして適切な単体テストがすでにこれをカバーするかどうか疑問に思い始めました?


6
単体テストでは、アサーションをアプリケーションコードから移動できます(したがって、混乱を避けられます)が、実際の運用システムで発生する可能性のあるすべてをチェックできないことを考慮してください。そのため、IMOコードコントラクトには、特に正確性が特に重要な重要なコードの場合にいくつかの利点があります。
ジョルジオ

基本的には開発時間、保守性、読みやすさ、コードカバレッジの向上でしょうか?
ロナッグ

1
私は主に、コードでアサーションを使用してパラメーターの正当性をチェックし(たとえば、null)、ユニットテストでさらにそれらのアサーションをチェックしています。
artjom

回答:


14

あなたはそれを「vs」と考えるべきではないと思います。
@Giorgioのコメントで述べられているように、コードコントラクトは(プロダクション環境で)不変条件をチェックし、ユニットテストはこれらの条件が満たされたときにコードが期待どおりに動作することを確認します。


2
条件が満たされないときにコードが機能するかどうかをテストすることも重要だと思います(たとえば、例外をスローします)。
svick

6

契約は、単体テストではできないことを少なくとも1つサポートします。パブリックAPIを開発しているとき、人々がどのようにコードを使用するかを単体テストすることはできません。ただし、メソッドのコントラクトを引き続き定義できます。

私は個人的に、モジュールのパブリックAPIを扱う場合にのみ、契約についてこのように厳密になります。他の多くの場合、おそらく努力する価値はありません(代わりに単体テストを使用できます)が、これは単なる意見です。

だからと言って、そうした場合に契約について考えないことを勧めるわけではありません。私はいつもそれらについて考えます。常に明示的にコーディングする必要があるとは思わない。


1

すでに述べたように、契約と単体テストには異なる目的があります。

契約は、前提条件が満たされていること、適切なパラメーターを使用してコードが呼び出されることなどを確認するための防衛プログラミングに関するものです。

さまざまなシナリオで、コードが機能することを確認する単体テスト。これらは「歯のある仕様」のようなものです。

アサートは、コードを堅牢にするのに適しています。ただし、多くのコードが追加されることが心配な場合は、デバッグ中に条件付きブレークポイントをいくつかの場所に追加して、アサートカウントを減らすこともできます。


0

checkVariants()呼び出しにあるものはすべて、テストから行うことができます。実際にどれだけの労力が必要か(外部依存関係、結合レベルなど)に依存しますが、1つの観点からコードをクリーンアップします。アサーションに対して開発されたコードベースがどの程度テスト可能であるかは、リファクタリングなしではわかりません。

@durosに同意します。これらは排他的または競合するアプローチと考えるべきではありません。実際、TDD環境では、「要件」アサーションにはテストが必要であると主張することさえできます:)。

しかし、アサートは、失敗したチェックを修正するために実際に何かをしない限り、コードをより堅牢にしません。通常、トラブルの最初の兆候で処理を中止することにより、データの破損などを止めます。

テスト駆動/十分にテストされたソリューションは、通常、相互作用するコンポーネントを開発し、問題の原因により近いものに対処しながら、悪い入力と出力の原因/原因の多くをすでに考え、発見しているでしょう。

ソースが外部であり、それを制御できない場合、他のコードの問題に対処するコードを乱雑にしないために、ソースとコンポーネントの間に何らかのデータクレンジング/アサーションコンポーネントを実装し、そこにチェックを入れることを検討することができます。

また、誰かが開発した何らかのxUnitや他のテストライブラリを持たないあなたの使用している言語に興味があります、私は最近すべてのものにほとんど何かがあると思いましたか?


0

単体テストとコードコントラクトに加えて、別の側面を強調したいと思いました。これは、最初にコードを誤って呼び出す可能性を排除または削減するようにインターフェイスを定義することの価値です。

常に簡単または可能というわけではありませんが、「このコードをより簡単にすることはできますか?」という質問を自問する価値はあります。

C#の作成者であるAnders Hejlsberg氏は、C#の最大の間違いの1つは、null不可の参照型を含まないことだと言いました。これは、多くの必要なガードコードの乱雑さが存在する主な理由の1つです。

それでも、必要かつ十分な量のガードコードのみを持つようにリファクタリングすると、私の経験では、より使いやすく保守可能なコードになります。

残りの部分で@durosに同意します。


0

両方を行いますが、いくつかの静的ヘルパーメソッドを作成して、意図を明確にします。これは、GoogleがJavaに対して行ったことです。code.google.com/ p / guava-libraries / wiki / PreconditionsExplainedをご覧ください。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.