静的型付け機能コードの単体テスト


15

haskell、scala、ocaml、nemerle、f#、またはhaXeで記述された静的に型付けされた機能コードを単体テストするのが理にかなっています(最後は本当に興味のあることですが、より大きなコミュニティの知識を活用してください)。

私の理解から:

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

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

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

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

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


4
ちなみに、QuickCheckを試したことがない場合は、絶対にすべきです。
ジョンパーディ

scalacheck.orgが等価であるスカラ
Vランプ

回答:


8

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

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

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

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

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

エンジニアリングでは、ほとんどの安全システムは冗長性に依存しています。たとえば、車の2つの休憩、スカイダイバーの冗長パラシュートなど。ユニットテストにも同じ考え方が当てはまります。もちろん、要件が変更されたときに変更するコードが増えると、デメリットになる可能性があります。したがって、特に単体テストでは、それらを乾燥した状態に保つことが重要です(「Dont Repeat Yourself」の原則に従ってください)。静的に型付けされた言語では、弱く型付けされた言語よりも少ないユニットテストを記述する必要があります。特に「正式な」テストは必要ないかもしれません-これは良いことです。それは、実質的なことをテストする重要な単体テストに取り組む時間をより多く与えるからです。また、静的な型だからといって、単体テストが不要だとは思わないでください。リファクタリング中にバグを導入する余地がまだ十分にあります。


5

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

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

このようなプログラミングスタイルを使用する場合、単体テストをどの程度使用し、その理由は何ですか(コードでどの程度の品質を得たいと考えていますか)。

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

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


1
QuickCheckについてもう少し詳しく:基本的な考え方は、「プロパティ」(コードの不変式)を記述し、潜在的な入力を生成する方法を指定することです。その後、クイックチェックは大量のランダム入力を作成し、すべてのケースで不変式が保持されることを確認します。単体テストよりも徹底的です。
ティコンジャービス

1

単体テストは、コードの使用方法の例と、なぜ価値があるのか​​の説明として考えることができます。

これは、ボブおじさんがジョン・コンウェイの「ゲーム・オブ・ライフ」で私とペアリングするのに十分親切だった例です。これはこの種のことにとっては素晴らしいエクササイズだと思う。ほとんどのテストはシステム全体であり、ゲーム全体をテストしますが、最初のテストでは、1つの関数(セルの周囲の近傍を計算する関数)のみをテストします。すべてのテストが宣言形式で記述されており、探している動作が明確に記述されていることがわかります。

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


0

はい、ユニットテストは静的に型指定された機能コードで既に意味を持ちます。簡単な例:

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

prop_encode静的型で強制できます。

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