予測が困難なコードの単体テストをどのように作成しますか?


124

関数の正確な結果を事前に予測するのが難しい、非常に数値的/数学的なプログラムを頻繁に使用しています。

この種のコードでTDDを適用しようとすると、そのコードの単体テストを書くよりもテスト対象のコードを書く方がはるかに簡単だと思うことがよくあります。期待される結果を見つける唯一の方法は、アルゴリズム自体を適用することです頭の上、紙の上、またはコンピューターで)。これは間違っていると感じます。なぜなら、テスト中のコードを効果的に使用して、単体テストを検証するためであり、その逆ではありません。

テスト対象のコードの結果を予測することが困難な場合に、単体テストを作成し、TDDを適用するための既知の技術はありますか?

結果を予測するのが難しいコードの(実際の)例:

機能weightedTasksOnTime日あたりの仕事量が与えられると、workPerDay範囲(0、24]、現在時刻initialTime> 0、およびタスクのリストtaskArrayプロパティ完了するまでの時間と各; time> 0、期日due、及び重要度をimportance、リターン彼らの前に完了することができるタスクの重要性を示す範囲[0、1]に正規化された値dueの各タスクは、場合によって与えられた順序で完了した場合、日付taskArrayから開始し、initialTime

この関数を実装するアルゴリズムは比較的簡単です:でタスクを繰り返しtaskArrayます。各タスクについて、に追加timeinitialTimeます。新しい時間<の場合、アキュムレーターdueに追加importanceします。時間は逆workPerDayによって調整されます。アキュムレータを返す前に、タスクの重要度の合計で除算して正規化します。

function weightedTasksOnTime(workPerDay, initialTime, taskArray) {
    let simulatedTime = initialTime
    let accumulator = 0;
    for (task in taskArray) {
        simulatedTime += task.time * (24 / workPerDay)
        if (simulatedTime < task.due) {
            accumulator += task.importance
        }
    }
    return accumulator / totalImportance(taskArray)
}

上記の問題は、コアを維持しつつworkPerDay、正規化要件を削除することで単純化できると考えています。

function weightedTasksOnTime(initialTime, taskArray) {
    let simulatedTime = initialTime
    let accumulator = 0;
    for (task in taskArray) {
        simulatedTime += task.time
        if (simulatedTime < task.due) {
            accumulator += task.importance
        }
    }
    return accumulator
}

この質問は、テスト対象のコードが既存のアルゴリズムの再実装ではない状況に対処します。コードが再実装の場合、アルゴリズムの既存の信頼できる実装は自然なテストオラクルとして機能するため、本質的に結果を簡単に予測できます。


4
結果を予測するのが難しい関数の簡単な例を提供できますか?
ロバートハーベイ

62
FWIWは、アルゴリズムをテストしていません。おそらくそれは正しいです。実装をテストしています。多くの場合、手作業での作業は平行構造として問題ありません。
クリスチャンH


7
アルゴリズムを合理的に単体テストできない状況があります-たとえば、実行時間が複数の日/月である場合。これは、NP問題を解決するときに発生する可能性があります。これらのケースでは、あり、正式なを提供するために、より実現可能であることを証明コードが正しいことを。
ハルク

12
私が非常にトリッキーな数値コードで見たものは、単体テストを回帰テストとしてのみ扱うことです。関数を作成し、いくつかの興味深い値に対して実行し、結果を手動で検証してから、期待される結果から回帰をキャッチする単体テストを作成します。コーディングの恐怖?他の人がどう思うか興味があります。
チュウ

回答:


251

テストが難しいコードでテストできるものは2つあります。まず、縮退したケース。タスク配列に要素がない場合、または1つまたは2つだけで、1つが期限を過ぎている場合など、実際の問題よりも単純ですが、手動で計算するのが妥当な場合はどうなりますか。

2つ目は、健全性チェックです。これらは、答えが正しいかどうかわからない場合に行うチェックですが、間違っているかどうかは間違いなくわかります。これらは、時間を進めなければならない、値が妥当な範囲になければならない、パーセンテージが合計で100になるなどのようなものです。

はい、これは完全なテストほどではありませんが、健全性チェックとケースの縮退をめちゃくちゃにすると、完全なアルゴリズムの問​​題が明らかになります。


54
これは非常に良いアドバイスだと思います。これらの種類の単体テストを書くことから始めます。ソフトウェアの開発中にバグや間違った答えを見つけた場合は、それらを単体テストとして追加してください。間違いなく正しい答えを見つけたら、ある程度は同じことをしてください。時間をかけてそれらを構築し、あなたは(最終的に)彼らは...するつもりだったかわからない始めにもかかわらず、ユニットテストの非常に完全なセットを持っています
Algyテイラー

21
場合によっては役立つかもしれないもう1つのことは(おそらくこれではないかもしれませんが)、逆関数を記述し、連鎖したときに入力と出力が同じであることをテストすることです。
サイバー

7
健全性チェックは、多くの場合、QuickCheck
jk

10
私がお勧めするテストのもう1つのカテゴリは、出力の意図しない変更を確認するためのいくつかのカテゴリです。これらの目的は、出力ニュートラルの変更を意図したものが意図せずにアルゴリズムの動作に影響を与えたことをフラグすることでメンテナーを支援することであるため、コード自体を使用して期待される結果を生成することでこれらを「チート」できます。
ダン・ニーリー

5
@iFlo冗談を言っているかどうかはわかりませんが、逆の逆関数は既に存在します。ただし、テストの失敗は逆関数の問題である可能性があることを理解する
価値

80

私は、予測が困難な出力を使用して科学ソフトウェアのテストを作成していました。私たちは、メタモルフィックリレーションを多く使用しました。基本的に、正確な数値出力がわからなくても、ソフトウェアの動作について知っていることがあります。

あなたの場合の可能な例:あなたが毎日できる仕事の量を減らした場合、あなたができる仕事の総量はせいぜい同じままですが、おそらく減ります。したがって、のいくつかの値に対して関数を実行workPerDayし、関係が成り立つことを確認してください。


32
変容関係は、プロパティベースのテストの特定の例であり、一般にこれらのような状況に役立つツールです
-Dannnno

38

他の回答には、エッジまたはエラーの場合のテストを開発するための良いアイデアがあります。他の人にとっては、アルゴリズム自体の使用は理想的ではないことは明らかですが、それでも有用です。

アルゴリズム(または依存するデータ)が変更されたかどうかを検出します

変更が偶然の場合、コミットをロールバックできます。変更が意図的なものであった場合は、単体テストを再検討する必要があります。


6
そして記録のために、これらの種類のテストは、目的ごとに「回帰テスト」と呼ばれることが多く、基本的にはあらゆる修正/リファクタリングのセーフティネットです。
Pac0

21

他の種類のコードの単体テストを書くのと同じ方法:

  1. いくつかの代表的なテストケースを見つけてテストします。
  2. エッジケースを見つけてテストします。
  3. エラー状態を見つけてテストします。

コードにランダムな要素が含まれているか、決定論的でない(つまり、同じ入力に対して同じ出力が生成されない)場合を除き、コードはユニットテスト可能です。

副作用や、外力の影響を受ける機能を避けてください。純粋な関数はテストが簡単です。


2
非決定性アルゴリズムの場合、RNGのシードを保存するか、固定シーケンスまたは低不一致の決定性シリーズ(Haltonシーケンス
-wondra

14
@PaintingInAirアルゴリズムの出力を検証することが不可能な場合、アルゴリズム正しくないことありますか?
WolfgangGroiss

5
Unless your code involves some random elementここでの秘Theは、乱数ジェネレーターを注入された依存関係にすることです。そのため、希望する正確な結果を与える数値ジェネレーターに置き換えることができます。これにより、正確に再度テストすることができます-生成された数値も入力パラメーターとしてカウントします。not deterministic (i.e. it won't produce the same output given the same input)単体テストは制御された状況から開始する必要があるため、ランダム要素がある場合にのみ非決定的である可能性があります-その後、それを注入できます。ここで他の可能性を考えることはできません。
18年

3
@PaintingInAir:どちらか。私のコメントは、高速実行または高速テスト作成の両方に適用されます。手作業で1つの例を計算するのに3日かかる場合(コードを使用しない最も高速な方法を使用すると仮定しましょう)-3日かかります。代わりに、予想されるテスト結果を実際のコード自体に基づいている場合、テスト自体が危険にさらされます。それはやるようなものでif(x == x)、無意味な比較です。相互に独立するためには、2つの結果(実際:コードから得られるもの、期待されるもの:外部の知識から得られるもの)が必要です。
18年

2
仕様に準拠し、コンプライアンスを測定できる(例:ランダムな分布と広がり)場合は、確定的でなくてもユニットテスト可能です。異常のリスクを排除するには、非常に多くのサンプルが必要な場合があります。
mckenzm

17

投稿されたコメントによる更新

元の答えは簡潔にするために削除されました-編集履歴で見つけることができます。

PaintingInAirコンテキスト:起業家および学術者として、私が設計するアルゴリズムのほとんどは、私以外の誰からも要求されていません。質問で与えられた例は、タスクの順序付けの品質を最大化するための派生物のないオプティマイザーの一部です。内部的にサンプル関数の必要性をどのように説明したかという点に関しては、「時間どおりに完了するタスクの重要性を最大化するために、目的関数が必要です」。ただし、この要求と単体テストの実装との間にはまだ大きなギャップがあるようです。

まず、TL; DRを使用して、長い回答を避けます。

このように考えてください:
顧客はマクドナルドに入り、レタス、トマト、ハンドソープをトッピングしたハンバーガーを求めます。この注文はコックに与えられ、コックは要求どおりにバーガーを作ります。顧客はこのバーガーを受け取り、それを食べてから、これがおいしいバーガーではないことを料理人に訴えます!

これは料理人のせいではありません-彼は顧客が明示的に求めたことをしているだけです。要求された注文が実際においしいかどうかを確認するのは料理人の仕事ではありません。料理人は、顧客が注文したものを作成するだけです。おいしいものを注文するのは顧客の責任です

同様に、アルゴリズムの正確さを疑問視するのは開発者の仕事ではありません。彼らの唯一の仕事は、要求に応じてアルゴリズムを実装することです。
単体テストは開発者のツールです。ハンバーガーが注文と一致することを確認します(キッチンを出る前)。注文されたハンバーガーが実際においしいことを確認しようとはしません(するべきではありません)。

場合でも、あなたが顧客と料理の両方である、との意味の区別がまだあります。

  • 私はこの食事をきちんと準備しなかった、それはおいしくなかった(=調理エラー)。ステーキが好きな場合でも、焼けたステーキは決して美味しくなりません。
  • 私は食事をきちんと準備しましたが、私はそれが好きではありません(=顧客エラー)。ステーキが嫌いな人は、ステーキを完璧に調理したとしても、ステーキを食べるのは好きではないでしょう。

ここでの主な問題は、顧客と開発者(およびアナリスト-その役割は開発者でも表すことができますが)を分離していないことです。

コードのテストとビジネス要件のテストを区別する必要があります。

たとえば、顧客は[this]のように動作することを望んでいます。しかし、開発者は誤解しており、[それ]を行うコードを書いています。

開発者は、従ってかどうかをテストユニットテスト書き込む[すなわち]期待どおりに動作します。彼がアプリケーションを正しく開発した場合、顧客が期待していた[this]をアプリケーションが実行しなくても彼の単体テストに合格します。

顧客の期待(ビジネス要件)をテストする場合は、別の(およびそれ以降の)ステップで実行する必要があります。

これらのテストをいつ実行するかを示す簡単な開発ワークフロー:

  • 顧客は、解決したい問題について説明します。
  • アナリスト(または開発者)はこれを分析に書き留めます。
  • 開発者は、分析が説明することを行うコードを記述します。
  • 開発者は自分のコードをテストし(ユニットテスト)、分析に正しく従ったかどうかを確認します。
  • 単体テストが失敗した場合、開発者は開発に戻ります。ユニットテストがすべてパスするまで、これは無限にループします。
  • テスト済み(確認済みで合格)のコードベースができたので、開発者はアプリケーションをビルドします。
  • アプリケーションは顧客に提供されます。
  • 顧客は、与えられたアプリケーションが実際に解決しようとした問題を解決するかどうかをテストします(QAテスト)

顧客と開発者が同一の場合に、2つの別々のテストを行うことのポイントは何だろうと思うかもしれません。開発者から顧客への「ハンドオフ」がないため、テストは次々に実行されますが、それらはまだ別個のステップです。

  • 単体テストは、開発段階が終了したかどうかを確認するのに役立つ専用ツールです。
  • QAテストは、アプリケーションを使用して行われます

アルゴリズム自体が正しいかどうかをテストする場合、それは開発者の仕事の一部ではありません。それが顧客の関心事であり、顧客はアプリケーションを使用してこれをテストします。

起業家および学者として、あなたはここで重要な区別を見逃しているかもしれません。それは異なる責任を強調しています。

  • アプリケーションが、顧客が最初に要求した内容に従わない場合、通常、その後のコードの変更は無料で行われます。開発者のエラーだからです。開発者は間違いを犯し、それを修正するための費用を支払う必要があります。
  • アプリケーションは、(例えば、あなたが別の、より良いアルゴリズムを使用することにしました)顧客が最初に求めていたが、顧客は今、彼の心を変えた、コードベースへの変更がされているものをした場合はお客様ご負担とそうでないことから、顧客が現在望んでいるものとは異なる何かを求めたという開発者の責任。考えを変えるのは顧客の責任(コスト)であり、したがって、開発者が以前は合意されていなかったものを開発するためにより多くの努力を費やす必要があります。

「アルゴリズムを自分で考え出した場合」の状況については、これが問題を引き起こす可能性が最も高い状況だと思うので、もっと詳しく説明したいと思います。特に、「if A then B、else C」の例が提供されていない状況では。(ps私はdownvoterではありません)
PaintingInAir

@PaintingInAir:しかし、それはあなたの状況に依存するので、これについて詳しく説明することはできません。このアルゴリズムを作成することに決めた場合、特定の機能を提供するために作成したことは明らかです。誰があなたにそうするように頼んだのですか?彼らはリクエストをどのように説明しましたか?彼らは特定のシナリオで何をする必要があるかを教えてくれましたか?(この情報は、私が答えで「分析」と呼んでいるものです)受け取った説明(アルゴリズムの作成につながったもの)は、アルゴリズムが要求どおりに機能するかどうかをテストするために使用できます。つまり、コード/自己作成アルゴリズム以外のものを使用できます。
18年

2
@PaintingInAir:顧客、アナリスト、開発者を密に結びつけるのは危険です。あなたは問題の始まりを定義するような重要なステップをスキップする傾向があるので。それがあなたがここでやっていることだと思います。アルゴリズムが正しく実装されているかどうかではなく、アルゴリズムの正確さをテストしたいようです。しかし、それはあなたのやり方ではありません。実装のテストは、単体テストを使用して実行できます。アルゴリズム自体のテストは、(テスト済みの)アプリケーションを使用し、その結果を事実確認することです-この実際のテストは、コードベースの範囲外です(そうあるべきです)。
18年

4
この答えはすでに膨大です。元のコンテンツを再定式化する方法を見つけることを強くお勧めします。そうすることで、破棄したくない場合に新しい回答に統合できるようになります。
jpmc26

7
また、私はあなたの前提に同意しません。テストでは、コードが仕様に従って誤った出力を生成するタイミングを明らかにすることができます。テストでは、いくつかの既知のテストケースの出力を検証することが有効です。また、料理人は有効なハンバーガーの材料として「ハンドソープ」を受け入れるよりもよく知っている必要があり、雇用主はほとんど確実にどの材料が利用できるかについて料理人を教育しました。
jpmc26

9

プロパティテスト

数学関数は、従来の例に基づいた単体テストよりも「プロパティテスト」の方が適切な場合があります。たとえば、整数の「乗算」関数などの単体テストを書いているとします。関数自体は非常に単純に見えるかもしれませんが、それが乗算する唯一の方法である場合、関数自体のロジックなしでどのように徹底的にテストしますか?予想される入力/出力を持つ巨大なテーブルを使用できますが、これには制限があり、エラーが発生しやすくなります。

これらの場合、特定の期待される結果を探す代わりに、関数の既知のプロパティをテストできます。乗算では、負の数と正の数を乗算すると負の数になり、2つの負の数を乗算すると正の数になることなどを知っています。ランダム化された値を使用して、これらのプロパティがテスト値は、このような機能をテストするための良い方法です。通常、複数のプロパティをテストする必要がありますが、多くの場合、必ずしもすべてのケースで期待される結果を知ることなく、関数の正しい動作を一緒に検証するプロパティの有限セットを識別することができます。

私が見たプロパティテストの最も良い紹介の1つは、F#のこれです。構文がテクニックの説明を理解する上で障害にならないことを願っています。


1
ランダムなカルテット(a、b、c)を生成し、(ab)(cd)が(ac-ad)-(bc-bd)を生成することを確認するなど、再乗算の例に、より具体的なものを追加することをお勧めします。乗算演算はかなり壊れている可能性があり、(負の時間で負の収量が正の)ルールを維持しますが、分配ルールは特定の結果を予測します。
supercat

4

コードを記述して、結果が「正しく見える」かどうかを確認したいのですが、当然のことながら、それは良い考えではありません。

アルゴリズムが難しい場合、結果の手動計算を簡単にするためにいくつかのことを行うことができます。

  1. Excelを使用します。計算の一部またはすべてを実行するスプレッドシートをセットアップします。手順を確認できるように、十分にシンプルにしてください。

  2. メソッドをより小さなテスト可能なメソッドに分割し、それぞれ独自のテストを行います。小さい部品が機能していることが確認できたら、それらを使用して次の手順を手動で実行します。

  3. 集約プロパティを使用して健全性チェックを行います。たとえば、確率計算機があるとします。個々の結果がどうあるべきか分からないかもしれませんが、それらはすべて合計で100%にならなければならないことを知っています。

  4. 強引な。すべての可能な結果を​​生成するプログラムを作成し、アルゴリズムが生成するものよりも優れているものがないことを確認します。


3.では、ここでいくつかの丸め誤差を考慮してください。合計金額が100,000001%であるか、同様の厳密ではないが正確な数値である可能性があります。
18年

2
4についてはよくわかりません。可能なすべての入力の組み合わせに対して最適な結果を生成できる場合(確認のテストに使用します)、本質的に既に最適な結果を計算できるため、テストしようとしているこの2番目のコードが必要です。その時点で、既存の最適な結果ジェネレーターを使用することをお勧めします。既に機能していることが証明されています。(そして、まだ機能することがまだ証明されていない場合、その結果に頼って最初からテストを事実確認することはできません)。
18年

6
通常、@ flaterには、ブルートフォースが満たさない正確さと同様に、他の要件もあります。例えばパフォーマンス。
ユアン

1
@flaterあなたがそれを信じるなら、あなたの種類、最短経路、チェスエンジンなどを使うのは嫌だ。しかし、あなたの丸め誤差でidギャンブルが終日カジノを許可
Ewan

3
キングポーンのエンドゲームに着いたら、@ flaterを辞めますか?ゲーム全体がブルートフォースになれないからといって、個々のポジションができないわけではありません。1つのネットワークへの正しい最短パスをブルートフォースするからといって、すべてのネットワークで最短パスを知っているわけではありません。
ユアン

2

TL; DR

他の答えにはないアドバイスについては、「比較テスト」セクションに進んでください。


始まり

アルゴリズムによって拒否されるべきケース(workPerDayたとえば、ゼロまたはネガティブ)と些細なケース(たとえば、空のtasks配列)をテストすることから始めます。

その後、最も単純なケースを最初にテストします。tasks入力のために、異なる長さをテストする必要があります。0、1、および2要素をテストするのに十分である必要があります(2はこのテストのカテゴリ「多」に属します)。

精神的に計算できる入力を見つけることができれば、それは良い出発点です。私が時々使用する手法は、目的の結果から開始し、その結果を生成する入力に(仕様で)戻ることです。

比較試験

出力と入力の関係が明らかでない場合がありますが、1つの入力が変更された場合、異なる出力間に予測可能な関係があります。例が正しく理解されていれば、タスクを追加しても(他の入力を変更せずに)時間通りに行われる作業の割合は増加しないため、関数を2回呼び出すテストを作成できます。 -そして、2つの結果の間の不等式を主張します。

フォールバック

時々、仕様に対応するステップで手計算された結果を示す長いコメントに頼らなければなりませんでした(そのようなコメントは通常テストケースよりも長いです)。最悪の場合は、異なる言語または異なる環境で以前の実装との互換性を維持する必要がある場合です。時には、テストデータにのようなラベルを付ける必要があるだけです/* derived from v2.6 implementation on ARM system */。それはあまり満足のいくものではありませんが、移植時の忠実度テストとして、または短期的な松葉杖として受け入れられるかもしれません。

リマインダー

テストの最も重要な属性は読みやすさです-入力と出力が読み手にとって不透明な場合、テストの値は非常に低くなりますが、読み手がそれらの間の関係を理解するのを助けられる場合、テストは2つの目的を果たします。

不正確な結果(浮動小数点など)に対して適切な「ほぼ等しい」を使用することを忘れないでください。

オーバーテストを避ける-他のテストでは到達できないもの(境界値など)をカバーする場合にのみテストを追加します。


2

この種のテストが難しい機能については、特別なことは何もありません。同じことは、外部インターフェイスを使用するコードにも適用されます(たとえば、制御下になく、テストスイートでテストすることが確実にできないサードパーティアプリケーションのREST API、または、戻り値の正確なバイト形式)。

正しい入力に対してアルゴリズムを実行し、その動作を確認し、結果が正しいことを確認し、入力と結果をテストケースとしてカプセル化することは、非常に有効なアプローチです。いくつかのケースでこれを行うことができ、したがっていくつかのサンプルを取得できます。入力パラメーターをできるだけ異なるものにするようにしてください。外部API呼び出しの場合、実際のシステムに対していくつかの呼び出しを行い、いくつかのツールでそれらをトレースし、ユニットテストにそれらをモックして、プログラムがどのように反応するかを確認します。タスク計画コードを実行し、それらを手動で検証し、テストで結果をハードコーディングします。

次に、明らかに、(あなたの例では)タスクの空のリストのようなエッジケースを持ち込みます。そういうもの。

テストスイートは、結果を簡単に予測できる方法ほど優れていません。ただし、テストスイートなし(またはスモークテスト)よりも100%優れています。

ただし、結果正しいかどうかを判断するのが難しい場合、それはまったく別の問題です。たとえば、任意の大きな数が素数であるかどうかを検出するメソッドがあるとします。乱数を投げることはほとんどできず、結果が正しい場合は「見る」だけです(頭の中や紙の上で素数を決定できないと仮定します)。この場合、実際にできることはほとんどありません-既知の結果(つまり、いくつかの大きな素数)を取得するか、別のアルゴリズムで機能を実装する必要があります(おそらく別のチームでも-NASAは好きなようです)それ)、いずれかの実装にバグがある場合、少なくともバグが同じ間違った結果につながらないことを願っています。

これが通常のケースである場合は、要件エンジニアと十分に話し合う必要があります。彼らがあなたをチェックするのが簡単な(または可能な限り)方法であなたの要件を定式化できない場合、いつあなたは終了したかを知っていますか?


2

他の答えは良いので、私は彼らがこれまで集合的に見逃していたいくつかのポイントを打とうとします。

Synthetic Aperture Radar(SAR)を使用して画像処理を行うためのソフトウェアを作成(および完全にテスト)しました。それは本質的に科学的/数値的です(多くの幾何学、物理学、数学が関係しています)。

いくつかのヒント(一般的な科学/数値テスト用):

1)逆を使用します。fft[1,2,3,4,5]?わからない。なにifft(fft([1,2,3,4,5]))?する必要があります[1,2,3,4,5](またはそれに近い、浮動小数点エラーが出てくるかもしれません)。2Dの場合も同様です。

2)既知のアサートを使用します。行列式を作成する場合、行列式がランダムな100x100行列のものであると言うのは難しいかもしれません。しかし、恒等行列の行列式は、100x100であっても1であることを知っています。また、関数は、非可逆行列(すべて0でいっぱいの100x100など)で0を返す必要があることも知っています。

3)完全なアサートの代わりに大まかなアサートを使用します。画像間のマッピングを作成するタイポイントを生成し、それらを一致させるために画像間でワープを行うことにより、2つの画像を登録するSAR処理用のコードをいくつか作成しました。サブピクセルレベルで登録できます。先験的に、2つの画像の登録がどのように見えるかについては何も言うのは難しいです。どうやってテストできますか?次のようなもの:

EXPECT_TRUE(register(img1, img2).size() < min(img1.size(), img2.size()))

重複する部分にのみ登録できるため、登録された画像最小画像以下である必要あります。また、

scale = 255
EXPECT_PIXEL_EQ_WITH_TOLERANCE(reg(img, img), img, .05*scale)

自分自身に登録された画像は自分自身に近いはずですが、手元のアルゴリズムに起因する浮動小数点エラーよりも少し多く発生する可能性があるため、各ピクセルがピクセルの許容範囲の+/- 5%以内にあることを確認してください(0-255はグレースケールで、画像処理で一般的です)。結果は、少なくとも入力と同じサイズでなければなりません。

単にテストを吸うこともできます(つまり、テストを呼び出して、クラッシュしないことを確認します)。一般に、この手法は、テストを実行する前に最終結果を(簡単に)計算できない大規模なテストに適しています。

4)RNGの乱数シードを使用または保存します。

実行再現可能である必要があります。ただし、再現可能な実行を取得する唯一の方法は、乱数ジェネレーターに特定のシードを提供することです。ランダム性テストは価値がある場合があります。私は、ランダムに生成された縮退ケースで発生する科学的コードのバグを見た/聞いたことがあります(複雑なアルゴリズムでは、縮退ケースが何であるかさえわかりにくい場合があります)。常に同じシードを使用して関数を呼び出す代わりに、ランダムシードを生成し、そのシードを使用して、シードの値をログに記録します。この方法では、実行ごとにランダムシードが異なりますが、クラッシュした場合は、ログに記録したシードを使用して結果を再実行できます。私は実際にこれを実際に使用し、バグをつぶしたので、私はそれを言及するだろうと考えました。確かにこれは一度しか起こらなかったし、私はそれをする価値があるとは限らないことを確信しているので、慎重にこのテクニックを使用してください。ただし、同じシードを持つランダムは常に安全です。欠点(常に同じシードを常に使用するのではなく):テスト実行を記録する必要があります。利点:正確性とバグ修正。

あなたの特定のケース

1) が0を返すことをテストしtaskArray ます(既知のアサート)。

2)その結果、ランダム入力の生成 task.time > 0task.due > 0および task.importance > 0 すべてのために task Sを、その結果がより大きいアサート 0 (ラフアサート、ランダム入力)。夢中になってランダムなシードを生成する必要はありません。アルゴリズムはそれを保証するほど複雑ではありません。報われる可能性はほぼゼロです。テストを単純にしてください。

3)すべてのsについて、結果が(既知のアサート)かどうか task.importance == 0 テストしますtask 0

4)これについては他の回答もありましたが、特定のケースでは重要かもしれません。APIをチーム外のユーザーが使用する場合は、縮退したケースをテストする必要があります。たとえば、の場合workPerDay == 0、無効な入力であることをユーザーに伝える素敵なエラーをスローするようにしてください。APIを作成しておらず、それがあなたとあなたのチーム専用である場合は、おそらくこのステップをスキップして、退化したケースで呼び出すことを拒否することができます。

HTH。


1

アルゴリズムのプロパティベースのテストのために、ユニットテストスイートにアサーションテストを組み込みます。特定の出力を確認する単体テストの記述に加えて、メインコードでアサーションの失敗をトリガーすることで失敗するように設計されたテストを記述します。

多くのアルゴリズムは、アルゴリズムの各段階で特定のプロパティを維持することの正当性証明に依存しています。関数の出力を見ることでこれらのプロパティを適切にチェックできる場合、プロパティをテストするには単体テストで十分です。それ以外の場合、アサーションベースのテストでは、アルゴリズムが想定するたびに実装がプロパティを維持していることをテストできます。

アサーションベースのテストでは、アルゴリズムの欠陥、コーディングのバグ、数値の不安定性などの問題による実装の失敗が明らかになります。多くの言語には、コンパイル時またはコードが解釈される前にアサーションを除去するメカニズムがあり、実稼働モードで実行した場合にアサーションがパフォーマンスの低下を招くことはありません。コードが単体テストに合格したが、実際のケースでは失敗した場合、デバッグツールとしてアサーションをオンに戻すことができます。


1

ここの他の答えのいくつかは非常に良いです:

  • テストベース、エッジ、コーナーケース
  • 健全性チェックを実行する
  • 比較テストを実行する

...他にもいくつかの戦術を追加します。

  • 問題を分解します。
  • コード外でアルゴリズムを証明します。
  • [外部で証明された]アルゴリズムが設計どおりに実装されていることをテストします。

分解により、アルゴリズムのコンポーネントが期待どおりに動作することを確認できます。また、「適切な」分解により、それらが適切に接着されていることを確認できます。高度分解により、アルゴリズムを一般化および単純化して、徹底的なテストを書くのに十分な程度に(単純化された汎用アルゴリズムの)結果を手で予測できるようにします。

その程度まで分解できない場合は、自分と同僚、利害関係者、顧客を満足させるのに十分な方法で、コード外でアルゴリズムを証明してください。そして、実装が設計と一致することを証明するのに十分なだけ分解します。


0

これはいくぶん理想的な答えのように思えるかもしれませんが、さまざまな種類のテストを識別するのに役立ちます。

実装にとって厳密な回答が重要な場合は、アルゴリズムを説明する要件で実際に例と予想される回答を提供する必要があります。これらの要件はグループでレビューする必要があり、同じ結果が得られない場合は、理由を特定する必要があります。

実装者と同様にアナリストの役割を果たしている場合でも、実際に要件を作成し、単体テストを作成するずっと前にレビューする必要があります。この場合、期待される結果がわかり、それに応じてテストを作成できます。

一方、これが実装する部分であり、ビジネスロジックの一部ではないか、ビジネスロジックの回答をサポートしている場合、テストを実行して結果を確認し、テストを修正して期待どおりにする必要がありますそれらの結果。最終結果はすでに要件に対してチェックされているため、それらが正しい場合、それらの最終結果を供給するすべてのコードは数値的に正しい必要があり、その時点でユニットテストは、特定のアルゴリズムは正しい結果を生成します。


0

このプロセスに従うことは、場合によっては完全に受け入れられると思います。

  • テストケースを設計する
  • ソフトウェアを使用して答えを得る
  • 手で答えを確認してください
  • ソフトウェアの将来のバージョンが引き続きこの答えを出すように、回帰テストを作成します。

これは、第一原理から回答を手で計算するよりも、回答の正しさを手で確認する方が簡単な状況では、合理的なアプローチです。

印刷されたページをレンダリングするためのソフトウェアを書いている人々を知っており、印刷されたページに正確なピクセルが設定されていることを確認するテストを行っています。それを行う唯一の正しい方法は、ページをレンダリングするコードを記述し、見た目が良いかどうかを目で確認してから、将来のリリースの回帰テストとして結果をキャプチャすることです。

特定の方法論が最初にテストケースを書くことを推奨しているという本を読んだからといって、常にそうする必要があるわけではありません。規則は破られるべきです。


0

他の回答の回答には、特定の結果がテストされた関数の外で決定できない場合に、テストがどのように見えるかのテクニックが既にあります。

私が他の答えで見つけていない追加のことは、何らかの方法でテストを自動生成することです:

  1. 「ランダム」入力
  2. データの範囲全体の反復
  3. 一連の境界からのテストケースの構築
  4. 上記のすべて。

たとえば、関数がそれぞれ許容入力範囲[-1,1]を持つ3つのパラメーターを取る場合、各パラメーター{-2、-1.01、-1、-0.99、-0.5、-0.01、0,0.01のすべての組み合わせをテストします、0.5、0.99、1,1.01,2、(-1,1)でさらにランダム

要するに:たまに貧しい品質は数量によって助成することができます。

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