web-dev-qa-db-ja.com

静的に型付けされた関数コードの単体テスト

私は人々に聞いてみたかったのですが、その場合、haskell、scala、ocaml、nemerle、f#、haXeで書かれた静的型付き関数コードを単体テストするのが理にかなっています(最後に興味があるのは私ですが、より大きなコミュニティの知識を活用してください)。

私の理解から:

  • 単体テストの1つの側面は、仕様を実行可能な形式にすることです。ただし、正式な仕様を言語のセマンティクスに直接マップする宣言型スタイルを使用する場合、実際に仕様を実行可能な形式で別の方法で表現するpossibleとすると、価値が追加されますか?

  • 単体テストのより明白な側面は、静的分析では明らかにできないエラーを追跡することです。タイプセーフな関数コードは、静的アナライザーが理解するものに非常に近いコードを作成するための優れたツールであることを考えると、多くの安全性を静的分析にシフトできるように思えます。ただし、コードでx(どちらも座標)の代わりにyを使用するような単純な間違いはカバーできません。 OTOHのような間違いはテストコードの作成中にも発生する可能性があるため、努力する価値があるかどうかはわかりません。

  • 単体テストは冗長性をもたらします。つまり、要件が変更されると、要件を実装するコードとこのコードをカバーするテストの両方を変更する必要があります。もちろん、このオーバーヘッドはほぼ一定なので、実際には問題ではないと主張することもできます。実際、Rubyのような言語では、それは実際に利点と比較されませんが、静的型付けされた関数型プログラミングが多くの地上ユニットテストを対象とする方法をカバーすることを考えると、それは一定のオーバーヘッドは、ペナルティなしで単純に削減できます。

このことから、このプログラミングスタイルでは単体テストはやや時代遅れであると推測します。もちろん、そのような主張は宗教戦争につながるだけなので、これを簡単な質問に要約します。

そのようなプログラミングスタイルを使用する場合、ユニットテストをどの程度使用しますか、またその理由は何ですか(コードに対してどの程度の品質を期待していますか)。またはその逆:静的型付けされた関数コードのユニットをcoveredとして静的アナライザーで修飾できるため、単体テストカバレッジを必要としない基準はありますか?

15
back2dos

単体テストの1つの側面は、仕様を実行可能な形式にすることです。しかし、形式化された仕様を言語のセマンティクスに直接マッピングする宣言型スタイルを採用する場合、実際に仕様を実行可能な形式で別の方法で表現して、付加価値を与えることさえ可能ですか?

関数宣言に直接マッピングできる仕様がある場合-結構です。しかし、通常、これらは2つのまったく異なるレベルの抽象化です。単体テストは、関数に取り組んでいる同じ開発者によってホワイトボックステストとして記述された単一のコードをテストすることを目的としています。仕様は通常、「ここにこの値を入力してこのボタンを押すと、これが発生するはずです」のように見えます。通常、このような仕様では、複数の機能を開発してテストする必要があります。

ただし、コードでy(どちらも座標)ではなくxを使用するような単純な間違いはカバーできません。ただし、テストコードの記述中にもこのような間違いが発生する可能性があるため、努力する価値があるかどうかはわかりません。

あなたの誤解は、ユニットテストは実際にコードのバグを直接見つけるためのものであるということです。これらは、コードが進化したときに後でバグが発生するのを防ぐために作成されています。したがって、最初に関数をテストしてユニットテストを機能させ(「x」と「y」を適切に配置)、次にリファクタリング中にyではなくxを使用すると、ユニットテストでそれが表示されます。

単体テストは冗長性をもたらします。つまり、要件が変更されると、要件を実装するコードとこのコードをカバーするテストの両方を変更する必要があります。もちろん、このオーバーヘッドはほぼ一定なので、実際には問題ではないと主張することもできます。実際、Rubyのような言語では、それは実際に利点と比較されませんが、静的型付けされた関数型プログラミングが多くの地上ユニットテストを対象とする方法をカバーすることを考えると、それは一定のオーバーヘッドは、ペナルティなしで単純に削減できます。

エンジニアリングでは、ほとんどの安全システムは冗長性に依存しています。たとえば、車の2つの休憩、スカイダイバー用の冗長パラシュートなど。同じことがユニットテストにも当てはまります。もちろん、要件が変更されたときに変更するコードを増やすことは不利な場合があります。したがって、特にユニットテストでは、これらを維持することが重要ですDRY(「Dont Repeat Yourself」の原則に従ってください)。静的に型付けされた言語では、弱く型付けされた言語。特に「正式な」テストは必要ない場合があります。これは、重要なものをテストする重要な単体テストに取り組む時間が増えるためです。静的な型であると考えないでください。単体テストは必要ありません。リファクタリング中にバグを導入する余地はまだたくさんあります。

8
Doc Brown

単体テストの1つの側面は、仕様を実行可能な形式にすることです。しかし、形式化された仕様を言語のセマンティクスに直接マッピングする宣言型スタイルを採用する場合、実際に仕様を実行可能な形式で別の方法で表現して、付加価値を与えることさえ可能ですか?

仕様をタイプ制約として完全に表現できる可能性はほとんどありません。

そのようなプログラミングスタイルを使用する場合、ユニットテストをどの程度使用しますか、またその理由は何ですか(コードに対してどの程度の品質を期待していますか)。

実際、このスタイルの主な利点の1つは、純粋な関数の単体テストが簡単になることです。外部状態を設定したり、実行後にチェックしたりする必要はありません。

多くの場合、関数の仕様(またはその一部)は、戻り値を引数に関連付けるpropertiesとして表すことができます。この場合、 QuickCheck (Haskellの場合)または ScalaCheck (Scalaの場合)を使用すると、これらのプロパティを言語の式として書き留め、ランダムな入力が保持されることを確認できます。

5
Alexey Romanov

ユニットテストは、コードを使用する方法の例と、なぜそれが重要であるかの説明と考えることができます。

例はここにあります ここでボブおじさんは私とペアにできるほど親切でした John Conwayの "Game of Life" 。こういうことにはいい運動だと思います。テストのほとんどはフルシステムであり、ゲーム全体をテストしますが、最初のテストは1つの関数(セルの周囲の近傍を計算する関数)のみをテストします。すべてのテストが宣言的な形式で記述されており、私たちが探している動作が明確に記述されていることがわかります。

関数で使用される関数をモックすることも可能です。それらを関数(依存性注入に相当)に渡すか、または Brian MarickのMidje のようなフレームワークを使用します。

1
Lunivore

はい、単体テストは静的に型付けされた関数コードで既に理にかなっています。簡単な例:

prop_encode a = (decode . encode $ a) == a

強制できますprop_encode静的タイプ。

0
Zhen