web-dev-qa-db-ja.com

テストがテストするコードとインラインで書かれていない理由はありますか?

私は Literate Programming について少し読んでいます、そしてそれは私に考えさせられました...よく書かれたテスト、特にBDDスタイルの仕様は、散文よりもコードが何であるかを説明するのにより良い仕事をすることができます、自分の正確さを検証する大きな利点があります。

テストするコードにインラインで記述されたテストを見たことがありません。これは、言語が同じソースファイルに記述されたときにアプリケーションとテストコードを簡単に分離できない傾向があるため(または誰も簡単に作成できないため)、または人々がアプリケーションコードからテストコードを分離するというより根本的な理由がありますか?

92
Chris Devereux

インラインテストで考えられる唯一の利点は、書き込むファイルの数を減らすことです。最近のIDEでは、これはそれほど大きな問題ではありません。

ただし、インラインテストにはいくつかの明らかな欠点があります。

  • 懸念の分離 に違反しています。これは議論の余地があるかもしれませんが、私にとって機能のテストはそれを実装することとは別の責任です。
  • テスト/実装を区別するために新しい言語機能を導入する必要があります。そうしないと、2つの間の線を曖昧にするリスクを負うことになります。
  • ソースファイルが大きくなると、操作が難しくなります。読みにくくなり、理解しにくくなり、ソース管理の競合に対処しなければならない可能性が高くなります。
  • いわば、「テスター」の帽子をかぶるのが難しくなると思います。実装の詳細を見ていると、特定のテストの実装をスキップしたくなるでしょう。
88
vaughandroid

私はいくつか考えることができます:

  • 読みやすさ。 「実際の」コードとテストを散在させると、実際のコードが読みにくくなります。

  • コード膨張。 「実際の」コードとテストコードを同じファイル/クラス/にコンパイルすると、コンパイルされたファイルが大きくなる可能性があります。これは、バインディングが遅い言語では特に重要です。

  • あなたの顧客/クライアントがあなたのテストコードを見ることを望まないかもしれません。 (私はlikeこの理由はありません...しかし、クローズドソースプロジェクトで作業している場合、テストコードはとにかく顧客を支援することはできません。)

現在、これらの各問題に対して考えられる回避策があります。しかし、IMO、そもそもそこに行かないほうが簡単です。


初期の頃は、Javaプログラマーがこの種のことを行っていました。たとえば、テストを容易にするためにクラスにmain(...)メソッドを含めることです。このアイデアはある種のテストフレームワークを使用してテストを個別に実装することは業界の慣例です。

(Knuthによって考案された)識字プログラミングがソフトウェアエンジニアリング業界で一度も行われたことがないことも注目に値します。

36
Stephen C

実際、あなたは Design By Contract をこれをしていると考えることができます。問題は、ほとんどのプログラミング言語で次のようなコードを記述できないことです:(前提条件を手動でテストするのは非常に簡単ですが、ポスト条件は、コードの記述方法を変更せずに大きな課題です(巨大なネガティブIMO)。

Michael Feathersには これに関するプレゼンテーション があり、これは、コード品質を向上させることができると彼が言及する多くの方法の1つです。

14
Daniel Kaplan

コード内のクラス間の密結合を回避しようとするのと同じ理由の多くのために、テストとコード間の不要な結合を回避することも良い考えです。

Creation:テストとコードは、さまざまな人がさまざまなタイミングで作成できます。

Control:要件を指定するためにテストを使用する場合、実際のコードとは異なり、誰がいつ変更できるかについて異なるルールを適用する必要があります。

再利用性:テストをインラインで配置すると、別のコードでテストを使用できなくなります。

正しく機能するコードのチャンクがあり、パフォーマンス、保守性などの面で多くのことが望まれているとします。そのコードを新しい改良されたコードに置き換えることにしました。同じテストセットを使用すると、新しいコードが古いコードと同じ結果を生成することを確認できます。

選択可能性:テストをコードから分離しておくと、実行するテストを簡単に選択できます。

たとえば、現在作業中のコードにのみ関連する小さなテストスイートと、プロジェクト全体をテストする大きなスイートがあるとします。

13
Caleb

ここに私が考えることができるいくつかの追加の理由があります:

  • テストを別のライブラリに置くと、そのライブラリのみをテストフレームワークに対してリンクし、本番コードをリンクするのが簡単になります(これは一部のプリプロセッサによって回避できますが、より簡単なソリューションがテストを書き込むことである場合にそのようなものを構築する理由別の場所)

  • 関数、クラス、ライブラリのテストは通常​​、「ユーザー」の視点(その関数/クラス/ライブラリのユーザー)から作成されます。このような「コードを使用する」は通常、別のファイルまたはライブラリに書き込まれ、その状況を模倣する場合、テストはより明確または「より現実的」になる場合があります。

10
Doc Brown

テストがインラインの場合、製品を顧客に出荷するときに、テストに必要なコードを削除する必要があります。したがって、テストを格納する追加の場所は、コードyoと、customerが必要とするコードを単に区別するだけです。

5
mhr

このアイデアは、単にオブジェクトベースまたはオブジェクト指向の設計のコンテキスト内の「Self_Test」メソッドに相当します。 Adaのようなコンパイルされたオブジェクトベースの言語を使用している場合、すべてのセルフテストコードは、本番環境のコンパイル中に未使用(呼び出されない)としてコンパイラーによってマークされます。そのため、すべてが最適化されて削除されます。結果の実行可能ファイル。

「Self_Test」メソッドを使用することは非常に良いアイデアであり、プログラマーが本当に品質に関心がある場合、彼らはすべてそれを行うでしょう。ただし、1つの重要な問題は、「Self_Test」メソッドは、実装の詳細にアクセスできず、代わりにオブジェクトの仕様内の他のすべての公開されたメソッドのみに依存する必要があるという点で、厳しい規律を持つ必要があることです。明らかに、セルフテストが失敗した場合、実装を変更する必要があります。セルフテストは、オブジェクトのメソッドのすべての公開されたプロパティを厳密にテストする必要がありますが、特定の実装の詳細に決して依存することは決してありません。

オブジェクトベースの言語とオブジェクト指向の言語は、テストされたオブジェクトの外部のメソッドに関して正確にそのタイプの規律を提供することがよくあります(オブジェクトの仕様を適用し、実装の詳細へのアクセスを防止し、そのような試みが検出された場合にコンパイルエラーを発生させます) )。ただし、オブジェクト自体の内部メソッドには、すべての実装詳細への完全なアクセス権が与えられています。したがって、セルフテストメソッドは独特の状況にあります。その性質上、内部メソッドである必要があります(セルフテストは明らかにテストされるオブジェクトのメソッドです)が、外部メソッドのすべてのコンパイラ規則を受け取る必要があります(オブジェクトの実装の詳細から独立している必要があります)。オブジェクトの内部メソッドを外部メソッドのように統制する機能を提供するプログラミング言語はほとんどありません。したがって、これは重要なプログラミング言語設計の問題です。

適切なプログラミング言語がサポートされていない場合、最適な方法は、コンパニオンオブジェクトを作成することです。つまり、コーディングするすべてのオブジェクト(「Big_Object」と呼ぶことにします)に対して、「実際の」オブジェクトの名前(この場合は「Big_Object_Self_Test」)と連結された標準のサフィックスで構成される名前を持つ2番目のコンパニオンオブジェクトも作成します。 ")、およびその仕様が単一のメソッドで構成される(" Big_Object_Self_Test.Self_Test(This_Big_Object:Big_Object)return Boolean; ")。その後、コンパニオンオブジェクトはメインオブジェクトの仕様に依存し、コンパイラはコンパニオンオブジェクトの実装に対してその仕様のすべての規則を完全に適用します。

5
commenter8

これは、リリースビルドからテストコードを削除するのは難しいから不可能であるので、インラインテストは行われないことを示唆する多数のコメントへの応答です。これは真実ではありません。ほとんどすべてのコンパイラとアセンブラはこれをすでにサポートしています。C、C++、C#などのコンパイルされた言語では、これはコンパイラディレクティブと呼ばれるもので行われます。

C#の場合(私はc ++も同様ですが、使用しているコンパイラーによっては構文が少し異なる可能性があります)これがあなたのやり方です。

#define DEBUG //  = true if c++ code
#define TEST /* can also be defined in the make file for c++ or project file for c# and applies to all associated .cs/.cpp files */

//somewhere in your code
#if DEBUG
// debug only code
#Elif TEST
// test only code
#endif

これはコンパイラディレクティブを使用するため、フラグが設定されていない場合、ビルドされる実行可能ファイルにはコードが存在しません。これは、複数のプラットフォーム/ハードウェア用の「1回書き込み、2回コンパイル」プログラムを作成する方法でもあります。

5
john

Perlコードでインラインテストを使用します。インラインコードからテストファイルを生成するモジュール Test :: Inline があります。

私は特にテストの整理が得意ではありませんが、インライン化するとテストが簡単になり、維持しやすくなります。

提起されたいくつかの懸念への対応:

  • インラインテストはPODセクションで記述されているため、実際のコードの一部ではありません。これらはインタープリターによって無視されるため、コードの膨張はありません。
  • Vimfolding を使用して、テストセクションを非表示にします。表示されるのは、+-- 33 lines: #test----のように、テストされている各メソッドの上の1行だけです。テストで作業したい場合は、テストを展開するだけです。
  • Test :: Inlineモジュールは、テストを通常のTAP互換ファイルに「コンパイル」して、従来のテストと共存できるようにします。

参考のために:

2
mla

テストを分離するもう1つの理由は、実際の実装ではなく、追加または異なるライブラリをテストに使用することが多いためです。テストと実装を混在させると、実装でのテストライブラリの誤った使用がコンパイラによって検出されなくなります。

また、テストには、テストする実装部分よりもはるかに多くのコード行がある傾向があるため、すべてのテスト間で実装を見つけるのは困難です。 :-)

1

Erlang 2は実際にはインラインテストをサポートしています。使用されていない(変数に割り当てられている、または渡されているなど)コード内のブール式は、自動的にテストとして扱われ、コンパイラーによって評価されます。式がfalseの場合、コードはコンパイルされません。

1
Mark Rendle

これは真実ではありません。量産コードが特に純粋な場合は、量産コードの横にユニットテストを配置する方がはるかに優れています。

たとえば.NETで開発している場合、テストコードを本番アセンブリに配置し、出荷前に Scalpel を使用してそれらを削除できます。

0
zumalifeguard