単体テストを同じプロジェクトまたは別のプロジェクトに配置しますか?


137

単体テストを同じプロジェクトに便宜上入れますか、それとも別のアセンブリに入れますか?

私たちのようにそれらを別のアセンブリに配置すると、ソリューション内に多数の追加プロジェクトが作成されます。コーディング中の単体テストには最適ですが、これらの余分なアセンブリをすべて使用せずにアプリケーションをリリースするにはどうすればよいですか?

回答:


100

私の意見では、単体テストは本番コードとは別のアセンブリに配置する必要があります。単体テストを製品コードと同じアセンブリに配置することのいくつかの短所を次に示します。

  1. 単体テストは量産コードに付属しています。製品コードに同梱される唯一のものは製品コードです。
  2. アセンブリは、ユニットテストによって不必要に肥大化します。
  3. 単体テストは、自動ビルドや継続ビルドなどのビルドプロセスに影響を与える可能性があります。

私はどんなプロも知りません。追加のプロジェクト(または10)を持つことは、不利なことではありません。

編集:ビルドと配送に関する詳細情報

さらに、自動ビルドプロセスでは、本番環境と単体テストを別の場所に配置することをお勧めします。理想的には、ユニットテストビルドプロセスは、製品コードがビルドされ、製品ファイルをユニットテストディレクトリにコピーする場合にのみ実行されます。この方法でこれを行うと、実際のビットが配送などのために分離されます。さらに、この時点で特定のディレクトリのすべてのテストに対して自動ユニットテストを実行することは、かなり簡単です。

要約すると、ビットとその他のファイルの毎日のビルドとテストおよび出荷の一般的なアイデアは次のとおりです。

  1. 本番ビルドが実行され、本番ファイルが特定の「本番」ディレクトリに配置されます。
    1. 本番プロジェクトのみをビルドします。
    2. コンパイルされたビットとその他のファイルを「製品」ディレクトリにコピーします。
    3. ビットやその他のファイルをリリース候補ディレクトリにコピーします。別名、クリスマスリリースディレクトリは「Release20081225」になります。
  2. 本番ビルドが成功すると、単体テストビルドが実行されます。
    1. 製品コードを「tests」ディレクトリにコピーします。
    2. ユニットテストを「tests」ディレクトリにビルドします。
    3. 単体テストを実行します。
  3. ビルド通知と単体テストの結果を開発者に送信します。
  4. リリース候補(Release20081225など)が受け入れられたら、これらのビットを出荷します。

15
IMOあなたがリストする短所は常に適用できるとは限りません。同じプロジェクトの利点は、テスト済みのクラス+テストを簡単にグループ化できることです。これらの小さな便利さは、テストを作成する際に役立ちます。個人的な好みがここで勝ち、あなたのポイントは常に関連しているわけではありません。
orip 08

7
出荷時にテストを削除する必要があるかどうかを最初に確認する必要があります。はいの場合、別のプロジェクトが必要です。そうでない場合は、他の長所と短所を使用して決定します。テストを展開できないと考える人は、デフォルトで常に「個別のプロジェクト」の結論に到達します。
orip

7
単体テストのような非プロダクションコードを出荷する利点は見当たらず、欠点もたくさんあります。単体テストの出荷は、配布する必要があるより多くのビットを扱っていることを意味します。単体テストには、独立した一連の依存関係もあります。これで、NUnit、Rhino、Moqなどが出荷されます。それはさらに膨らみます。単体テストを別のプロジェクトに配置するには、ほんの少しの労力が必要であり、それは一度のコストです。単体テストは出荷すべきではないという結論にとても満足しています。
Jason Jackson

4
プロダクションコードで単体テストを削除するための「個別のプロジェクト」アプローチの代替手段として、などのカスタムシンボルとコンパイラディレクティブの使用を検討して#ifください。このカスタムシンボルは、CIソフトウェアスクリプトのコンパイラコマンドラインパラメータを使用して切り替えることができます。msdn.microsoft.com/en-us/library/4y6tbswk.aspxを参照してください
リッチC

23
.NETは、完全に別のプロジェクトでテストを行うという曲線の背後にあり、これはすぐに変更されると思います。リリースビルドのテストコードを無視するようにビルドプロセスを作成できなかった理由はありません。これを行ういくつかのWebアプリジェネレーターを使用しました。ユニットテストのコードファイルを、同じファイル階層全体にわたって記述されたコードファイルのすぐ横に含めるのが絶対に良いと思います。Googleでは「フラクタル」ファイル編成と呼ばれるこれを推奨しています。テストがインラインコメントやreadmeドキュメントと異なるのはなぜですか?コンパイラは後者を喜んで無視します。
Brandon Arnold

112

別のプロジェクトですが、同じソリューションです。(私はテストコードと製品コードの別々のソリューションを持つ製品に取り組んできました-それは恐ろしいことです。あなたは常に2つを切り替えています。)

別のプロジェクトの理由は、他の人が述べたとおりです。データ駆動型テストを使用している場合、テストを本番アセンブリに含めると、かなりの量の膨張が生じる可能性があることに注意してください。

製品コードの内部メンバーにアクセスする必要がある場合は、InternalsVisibleToを使用します。


+1:メインコードと同じプロジェクトでユニットテストに遭遇しました。実際のコードからテストを見つけるのは困難です-命名規則に従っているにもかかわらずです。
フェントン

32
経験の浅い読者にとって、これは[assembly:InternalsVisibleTo("UnitTestProjectName")]プロジェクトAssemblyInfo.csファイルに追加することを意味します。
マーガス、2014年

非常に便利なマーガスの追加。このコンテキストでInternalsVisibleToについて言及してくれたJonに感謝します。
ビョルンオットーVasbotten

1
@jropella:prodアセンブリに署名する場合、内部を可視化できるのはとにかく署名されたアセンブリだけです。そしてもちろん、完全に信頼してコードを実行している場合は、リフレクションを介してアクセスできます...
Jon Skeet

2
@jropella:リフレクションはとにかくInternalsVisibleTo気にしない...しかし、あなたが見ていないだろう署名アセンブリ信頼符号なしそれはコンパイル時に妨げているため、組立InternalsVisibleToを使用します。
Jon Skeet、2015年

70

本番用コードでテストをデプロイすることに対する頻繁な反対は理解していません。私は小さなマイクロキャップでチームを率いていました(14から130人に成長しました)。半ダースほどのJavaアプリがあり、特定の環境でテストを実行するためにフィールドにテストを展開することは非常に価値があることがわかりました異常な動作を示していたマシン。ランダムな問題が現場で発生し、コストなしで数千のユニットテストをミステリーに投入できることは非常に貴重で、多くの場合、インストールの問題、不安定なRAMの問題、マシン固有の問題、不安定なネットワークの問題、などなど。フィールドでテストを行うことは非常に貴重だと思います。また、ランダムな問題がランダムなタイミングでポップアップし、ユニットテストをすぐに実行するのを待って、そこにユニットテストを配置しておくと便利です。ハードドライブのスペースは安いです。データと関数を一緒に維持しようとするように(OO設計)、コードとテストを一緒に維持することには根本的に価値があると思います(関数+関数を検証するテスト)。

テストをC#/。NET / Visual Studio 2008の同じプロジェクトに入れたいのですが、それを達成するための十分な調査はまだしていません。

FooTest.csと同じプロジェクトにFoo.csを保持することの大きな利点の1つは、クラスに兄弟テストが欠落しているときに開発者に常に通知されることです。これにより、テスト駆動型のコーディング手法の改善が促進されます...穴がより明白になります。


17
統合テストでがどのように役立つかがわかりますが、単体テストの場合はどうでしょうか。あなたがそれらを正しく書くならば、彼らはマシンへの依存性を持っていてはいけません。
ジョエル・マクベス

+1同意する 私は主に.NETを行っているので、本番コードでテストを出荷すると、コンパイルとテストの継続的な統合ステップを減らすというすばらしい副作用があります:1)ビルドするプロジェクトが半分だけ、2)すべてのテストアセンブリがメインアプリケーションの代わりにテストランナーをパラメーター化する方が簡単です。もちろん、Mavenを使用する場合、この引数は適用されません。最後に、私の顧客はより技術的な人々であり、仕様に対する現在の状態を文書化しているため、彼ら自身がテストを実行できることを本当に望んでいます。
mkoertgen 2013年

TDD / BDDスタイルで統合テストを行うことは完全に理にかなっていると思います。つまり、NUnit、xUnitなどの標準的なテストランナーで簡単に自動化できるテストコードを記述します。もちろん、ユニットと統合テストを混同しないでください。ビルド検証(BVT)のために迅速かつ頻繁に実行するテストのセットと、統合テストのためのテストのセットを知っていることを確認してください。
mkoertgen 2013年

1
人々がそれに反対する理由は、それが彼らが慣れているものではないからです。初めてのユニットテストの経験が実稼働コード内にあることであり、実際には、テストが実稼働メソッドに関連付けられていることを示すプログラミング言語の特定の構文を使用している場合、開発ワークフローの非常に貴重な側面であると断言します。そして、それらを別のプロジェクトに入れるのは絶対にクレイジーです。ほとんどの開発者はこのようなものです。フォロワー。
zumalifeguard 2014

19

ユニットテストをコードと同じプロジェクトに配置して、より良いカプセル化を実現します。

内部メソッドを簡単にテストできます。つまり、内部メソッドであるはずのメソッドをパブリックにしないでください。

また、ユニットテストを作成しているコードの近くに配置することも非常に便利です。メソッドを作成すると、同じプロジェクト内にあるため、対応する単体テストを簡単に見つけることができます。unitTestsを含むアセンブリをビルドすると、unitTestにエラーがあるとコンパイラエラーが発生するため、ビルドするためだけにユニットテストを最新の状態に保つ必要があります。別のプロジェクトでunittestを使用すると、一部の開発者がunittest-projectのビルドを忘れ、壊れたテストがしばらく欠落する可能性があります。

また、コンパイルタグ(IF #Debug)を使用して、本番コードからユニットテストを削除できます。

自動統合テスト(made i NUnit)は単一のプロジェクトに属していないため、別のプロジェクトに含める必要があります。


1
しかし、それはReleaseビルドでテストを実行することができず、抜け落ちる「ヘイゼンバグ」がほとんどないことを意味します。
hIpPy 2013

3
フレンドアセンブリ(msdn.microsoft.com/en-us/library/0tke9fxk.aspx)を使用InternalsVisibleToすると、
個別の

3
@hlpPy-任意の条件付きコンパイルシンボルを構成でき、2つ以上のビルド構成を持つことができます。例えば; あなたが持っているかもしれませんDebugApprovalそしてRelease; そして、すべてのリリース最適化で承認をコンパイルします。また、通常、単体テストは最初の段階でのヘイゼンバグの検出には適していません(私の経験では)。あなたはそれらのために特定の統合回帰テストを必要とする傾向があります-そしてそれらを並べて配置することもできます。
Eamon Nerbonne、2016

別のプロジェクトでは、同じコンパイル時エラーが発生します。コードを深く理解する必要がある場合は、ユニットテストをコードにハックするよりも多くの抽象化が必要なように聞こえます。コードの条件文に依存してブランチを切り替えることも適切ではありません。ユニットテストと統合テストのように聞こえが混乱しています。
Nick Turner、

12

TypeScriptプロジェクトでいくつかの時間を費やした後、テストは、テストするコードと一緒にファイルに配置されることが多いため、個別に保つよりもこのアプローチを好むようになりました。

  • テストファイルに移動する方が速くなります。
  • テストするクラスの名前を変更すると、テストの名前を忘れずに覚えやすくなります。
  • テスト中のクラスを移動するときは、テストを移動する方が覚えやすいです。
  • クラスにテストがない場合はすぐにわかります。
  • テスト用とコード用の2つの重複するファイル構造を管理する必要はありません。

そのため、最近新しい.NET Coreプロジェクトを開始したとき、テストまたはテストアセンブリを最終リリースに同梱せずに、C#プロジェクトでこの構造を模倣できるかどうかを確認したいと思いました。

プロジェクトファイルに次の行を追加すると、これまでのところうまく機能しているようです。

  <ItemGroup Condition="'$(Configuration)' == 'Release'">
    <Compile Remove="**\*.Tests.cs" />
  </ItemGroup>
  <ItemGroup Condition="'$(Configuration)' != 'Release'">
    <PackageReference Include="nunit" Version="3.11.0" />
    <PackageReference Include="NUnit3TestAdapter" Version="3.12.0" />
    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.9.0" />
  </ItemGroup>

上記により、指定されたReleaseすべてのファイルが構成*.Tests.csからコンパイルから除外され、必要な単体テストパッケージ参照が削除されます。

それでもリリース構成でクラスを単体テストできるようにしたい場合は、のReleaseようなものから派生した新しい構成を作成するだけで済みますReleaseContainingTests


更新:しばらくこの手法を使用した後、エクスプローラーウィンドウでテスト(およびその他のもの)をさらに目立たせるためにVSコードでアイコンをカスタマイズすることも役立ちます。

VSコードのスクリーンショット

これを行うには、Material Icon Theme拡張機能を使用して、VS Code設定JSONに次のようなものを追加します。

"material-icon-theme.files.associations": {
  "*.Tests.cs": "test-jsx",
  "*.Mocks.cs": "merlin",
  "*.Interface.cs": "yaml",
}

まさに今始めたばかりのこと
マシュースティープルズ

これは.net標準クラスライブラリでも機能しますか?
Zenuka

10

私の単体テストは常に別のプロジェクトで行われます。実際、ソリューションに含まれるすべてのプロジェクトについて、それに対応する個別のテストプロジェクトがあります。テストコードはアプリケーションコードではなく、コードと混在させないでください。少なくともTestDriven.Netを使用して、それらを別々のプロジェクトに保持することの1つの利点は、テストプロジェクトを右クリックしてそのプロジェクトのすべてのテストを実行し、アプリケーションコードのライブラリ全体をワンクリックでテストできることです。


1
次に、内部クラスを単体テストするにはどうすればよいですか?または、公開クラスのみをテストしますか?
user19371 2008

7
<assembly:InternalsVisibleTo = "TestProject" />必要に応じて、ただし、通常はパブリックインターフェイスのみをテストします。
tvanfosson 2008

@ tvanfosson、Specs(Specflow)で何をしますか?別のプロジェクトを持っていますか、それとも単体テストプロジェクトに入れますか?+1。
w0051977

@ w0051977 Specflowを使用したことがないのでわかりません
tvanfosson

@tvanfosson、統合テストはどうですか?単体テストとは別のプロジェクトに入れますか?
w0051977

10

NUnitフレームワークが使用されている場合、同じプロジェクトにテストを配置する別の理由があります。単体テストを組み合わせた次の本番コードの例を考えてみます。

public static class Ext
{
     [TestCase(1.1, Result = 1)]
     [TestCase(0.9, Result = 1)]
     public static int ToRoundedInt(this double d)
     {
         return (int) Math.Round(d);
     }
}

ここでの単体テストは、テストされるコードのドキュメントおよび仕様として機能します。テストを別のプロジェクトに配置して、自己文書化のこの効果を達成する方法がわかりません。関数のユーザーは、テストを検索してそれらのテストケースを確認する必要がありますが、これはほとんどありません。

更新:このようなTestCase属性の使用がNUnitの開発者が意図したものではなかったことは知っていますが、なぜそうではないのですか?


2
私はこのアイデアが好きです。コードを使用して、入力と期待される出力のいくつかの例をすぐに確認できます。
プレストンマコーミック

3

同じプロジェクトと異なるプロジェクトの間で変動します。

ライブラリをリリースする場合、本番用コードでテストコードをリリースすることは問題ですが、それ以外の場合は通常そうではありません(ただし、試す前に強い心理的障壁があります)。

同じプロジェクトにテストを配置すると、テストとテスト対象のコードを簡単に切り替えられ、リファクタリングや移動が容易になります。


3

私はそれらを別々のプロジェクトに入れました。アセンブリの名前は、名前空間の名前を反映しています。これは、一般的な規則です。したがって、Company.Product.Feature.slnというプロジェクトがある場合、そのプロジェクトの出力(アセンブリ名)はCompany.Product.Feature.dllになります。テストプロジェクトはCompany.Product.Feature.Tests.slnで、Company.Product.Feature.Tests.dllを生成します。

それらを単一のソリューションに保持し、構成マネージャーを介して出力を制御するのが最善です。デフォルトのデバッグとリリースを使用する代わりに、メインブランチ(開発、統合、本番)ごとに名前付きの構成があります。構成を設定したら、構成マネージャーの[ビルド]チェックボックスをクリックして、構成を含めたり除外したりできます。(構成マネージャーを取得するには、ソリューションを右クリックし、構成マネージャーに移動します。)Visual StudioのCMにはバグがあることがあることに注意してください。数回、プロジェクトやソリューションファイルにアクセスして、作成したターゲットをクリーンアップする必要がありました。

さらに、チームビルドを使用している場合(他の.NETビルドツールも同じであると確信しています)、ビルドを名前付き構成に関連付けることができます。これは、たとえば「プロダクション」ビルドのユニットテストをビルドしない場合、ビルドプロジェクトはこの設定も認識し、そのようにマークされているためビルドしないことを意味します。

また、以前はビルドマシンからXCopyドロップオフを実行していました。スクリプトは、*。Tests.Dllという名前のすべてのコピーがデプロイされないようにするだけです。シンプルですがうまくいきました。


1

それらは別々にしておくといいでしょう。

言及されている他の理由に加えて、コードとテストを一緒にすると、テストカバレッジ数が歪んでしまいます。単体テストカバレッジについてレポートする場合-単体テストを実行するとテストがカバーされるため、レポートされるカバレッジは高くなります。統合テストのカバレッジについてレポートする場合、統合テストは単体テストを実行しないため、レポートされるカバレッジは低くなります。


テストをカバーするために使用されるテクノロジーに依存しませんか?つまり、OpenCover は、テスト自体が含まれている場合、テストによって実行された場合、カバーされたコード行を考慮します。これにより、テストに予期しない例外がある場合、カバーされていないものとしてフラグが立てられます。
Isaac Llopis

0

Robert LopezによるFlood NNライブラリのユニットテストフレームワークに本当に触発されました。単体テスト済みのクラスごとに異なるプロジェクトを使用し、これらすべてのプロジェクトを保持する1つのソリューションと、すべてのテストをコンパイルして実行するメインプロジェクトがあります。

きちんとしたことは、プロジェクトのレイアウトでもあります。ソースファイルはフォルダーにありますが、VSプロジェクトのフォルダーは下にあります。これにより、コンパイラごとに異なるサブフォルダを作成できます。すべてのVSプロジェクトにはコードが付属しているため、ユニットテストの一部またはすべてを実行するのは非常に簡単です。


0

私はこれが非常に古い質問であることを知っていますが、ここで私の経験を追加したいと思います。最近、単体テストの習慣を個別のプロジェクトから同じプロジェクトに変更します。

どうして?

まず、メインプロジェクトのフォルダー構造をテストプロジェクトと同じにする傾向があります。だから、私が下にファイルがある場合、私はProviders > DataProvider > SqlDataProvider.cs私のようなユニットテストプロジェクトで同じ構造を作成していますProviders > DataProvider > SqlDataProvider.Tests.cs

しかし、プロジェクトがますます大きくなると、ファイルをあるフォルダーから別のフォルダーに、またはあるプロジェクトから別のプロジェクトに移動すると、それらを単体テストプロジェクトと同期するのが非常に面倒になります。

第2に、テストするクラスからユニットテストクラスに移動するのが必ずしも簡単ではありません。これはJavaScriptとPythonではさらに困難です。

最近、私はそれを練習し始めました、私が作成したすべてのファイル(たとえばSqlDataProvider.cs)は、次のようなTestサフィックスを持つ別のファイルを作成していますSqlDataProvider.Tests.cs

最初はファイルとライブラリ参照が膨らむように見えますが、長期的には一目でムービングファイルシンドロームを排除し、テスト対象の候補であるすべてのファイルにペアファイルがあることを確認します.Tests接尾辞。個別のプロジェクトを調べる代わりに、テストファイルを(サイドバイサイドであるため)簡単にジャンプできます。

ビジネスルールを記述して、プロジェクト全体をスキャンし、.Testsファイルがないクラスを特定して、所有者に報告することもできます。また、テストランナーにターゲット.Testsクラスを簡単に伝えることもできます。

特にJsとPythonでは、異なるパスから参照をインポートする必要はありません。テストするターゲットファイルの同じパスを使用するだけで済みます。

私はしばらくこの方法を使用していますが、プロジェクトの新規参入者にとって、プロジェクトの規模と保守性と学習曲線の間のトレードオフは非常に合理的だと思います。


同意しません。アイテムをグループ化してから個々のクラスをテストする方がはるかに整理されています。あなたのプロバイダーをテストする場合; IProvider、ISqlProvider、およびMockSqlConnectionがあります。プロバイダーフォルダーには、各プロバイダーの単体テストがあります。あなたはユニットテストを深く掘り下げることができますが、コードではなくテストを書くだけで、プロを削除してテスト用のコードを書き始めるかもしれません。
Nick Turner、

0

他の人が答えたように- 別のプロジェクトでテストを行う

言及されていないことの1つは、ファイルnunit3-console.exe以外に対しては実際に実行できないという事実.dllです。

TeamCity経由でテストを実行することを計画している場合、これは問題を引き起こします。

コンソールアプリケーションプロジェクトがあるとします。コンパイルすると、実行可能ファイル.exebinフォルダーに返されます。

nunit3-console.exeあなたはそのコンソールアプリケーションで定義されたすべてのテストを実行することはできません。

つまり、コンソールアプリケーションはexeファイルを返し、クラスライブラリはdllファイルを返します。

私は今日これに少し気がついて、それは:(


0

最近dockerにデプロイしています。Dockerfileが/ srcディレクトリを簡単にコピーして/ testディレクトリを離れることができるとき、私は別のプロジェクトを持っていることの価値を見ません。しかし、私はdotnetに不慣れで、何かが足りない可能性があります。


-2

個別のプロジェクト。ただし、同じsvnを共有する必要があるかどうかについては、私自身で議論しています。現在、私はそれらに別々のsvnリポジトリを提供しています。

「MyProject」-プロジェクト自体

と呼ばれる

"MyProjectTests"-MyProjectに関連付けられたテスト用。

これはかなりクリーンで、プロジェクトへのコミットとテストへのコミットがまったく別であるという利点があります。また、テストをリリースしなくても、必要に応じてプロジェクトのsvnを引き渡すことができます。また、テストおよびプロジェクト用にブランチ/トランク/タグディレクトリを作成できることも意味します。

しかし、私は次のようなものを1つのsvnリポジトリーに、プロジェクトごとに持つ傾向にあります。

私のプロジェクト
| \トランク
| | \コード
| \テスト
| \タグ
| | \ 0.1
| | | \コード
| | \テスト
| \ 0.2
| | \コード
| \テスト
\ブランチ
  \ MyFork
   | \コード
    \テスト

他の人がこの解決策についてどう思うか知りたいです。


5
テストとアプリのコードに別々のコミットをする必要はありません。* DDスタイルのデザインを使用している場合は特に、コミットは両方に関係し、コミットには両方が関係します。
eddiegroves 2009年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.