ユニットテスト

ウィキペディアから、無料の百科事典
ナビゲーションにジャンプ 検索にジャンプ

コンピュータープログラミングでは単体テストソフトウェアテスト方法であり、ソースコードの個々のユニット(1つ以上のコンピュータープログラムモジュールのセットと、関連する制御データ、使用手順、および操作手順)をテストして、それらが使用に適しているかどうかを判断します。 。[1]

説明

単体テストは通常​​、ソフトウェア開発者によって作成および実行される自動テストであり、アプリケーションのセクション(「ユニット」と呼ばれる)がその設計を満たし、意図したとおりに動作することを確認します。[2]手続き型プログラミングでは、ユニットはモジュール全体である可能性がありますが、より一般的には個々の関数またはプロシージャです。オブジェクト指向プログラミングでは、ユニットは多くの場合、クラスなどのインターフェイス全体、または個々のメソッドです。[3]最初にテスト可能な最小ユニットのテストを作成し、次にそれらの間の複合動作を作成することで、複雑なアプリケーションの包括的なテストを構築できます。[2]

発生する可能性のある問題を切り分けるには、各テストケースを個別にテストする必要があります。メソッドスタブモックオブジェクト[4] 偽物テストハーネスなどの代替手段を使用して、モジュールを分離してテストするのに役立てることができます。

開発中、ソフトウェア開発者は、ユニットの正確さを検証するために、基準または良好であることがわかっている結果をテストにコーディングする場合があります。テストケースの実行中に、フレームワークは基準に失敗したテストをログに記録し、それらを要約で報告します。このために最も一般的に使用されるアプローチは、テスト-関数-期待値です。

パラメータ化されたテストを使用することで、単体テストの作成と保守を高速化できますこれらにより、異なる入力セットを使用して1つのテストを複数回実行できるため、テストコードの重複が減少します。通常はクローズドメソッドであり、不変の条件をテストする従来の単体テストとは異なり、パラメーター化されたテストは任意のパラメーターのセットを取ります。パラメータ化されたテストは、TestNGJUnit、およびそれに対応する.NetであるXUnitによってサポートされます。単体テストに適したパラメーターは手動で提供することも、テストフレームワークによって自動的に生成される場合もあります。近年、理論の概念を活用し、同じステップを実行するテストケースを使用して、より強力な(単体)テストを作成するためのサポートが追加されましたが、入力セットで同じ実行ステップを使用する通常のパラメーター化されたテストとは異なり、実行時に生成されたテストデータを使用します事前定義されています。[5] [6] [7]

利点

単体テストの目標は、プログラムの各部分を分離し、個々の部分が正しいことを示すことです。[1]単体テストは、コードの一部が満たさなければならない厳密な書面による契約を提供します。結果として、それはいくつかの利点をもたらします。

単体テストでは、開発サイクルの早い段階で問題が見つかります。これには、プログラマーの実装のバグと、ユニットの仕様の欠陥または欠落部分の両方が含まれます。徹底的なテストのセットを作成するプロセスでは、作成者は入力、出力、およびエラー条件を検討する必要があり、したがって、ユニットの望ましい動作をより明確に定義します。コーディングを開始する前、またはコードを最初に記述したときにバグを見つけるコストは、後でバグを検出、識別、および修正するコストよりもかなり低くなります。リリースされたコードのバグも、ソフトウェアのエンドユーザーにコストのかかる問題を引き起こす可能性があります。[8] [9] [10]記述が不十分な場合、コードを単体テストすることは不可能または困難になる可能性があります。したがって、単体テストでは、開発者が関数やオブジェクトをより適切に構造化する必要があります。

エクストリームプログラミングスクラムの両方で頻繁に使用されるテスト駆動開発(TDD)では、コード自体が記述される前に単体テストが作成されます。テストに合格すると、そのコードは完了したと見なされます。コードが変更されたとき、またはビルドによる自動化されたプロセスを介して、より大きなコードベースが開発されると、同じ単体テストがその機能に対して頻繁に実行されます。単体テストが失敗した場合は、変更されたコードまたはテスト自体のバグであると見なされます。単体テストでは、障害または障害の場所を簡単に追跡できます。単体テストは、コードをテスターまたはクライアントに渡す前に開発チームに問題を警告するため、潜在的な問題は開発プロセスの早い段階で検出されます。

単体テストを使用すると、プログラマーはコードをリファクタリングしたり、システムライブラリを後日アップグレードしたり、モジュールが引き続き正しく機能することを確認したりできます(回帰テストなど)。手順は、変更によって障害が発生した場合にいつでも迅速に識別できるように、すべての関数メソッドのテストケースを作成することです。単体テストは、設計契約を破る可能性のある変更を検出します。

ユニットテストは、ユニット自体の不確実性を減らす可能性があり、ボトムアップテストスタイルのアプローチで使用できます。プログラムの各部分を最初にテストしてから、その部分の合計をテストすることにより、統合テストがはるかに簡単になります。[要出典]

ユニットテストは、システムの一種の生きたドキュメントを提供します。ユニットによって提供される機能とその使用方法を知りたい開発者は、ユニットテストを調べて、ユニットのインターフェイス( API )の基本を理解することができます[要出典]

ユニットテストケースは、ユニットの成功に不可欠な特性を具体化します。これらの特性は、ユニットの適切/不適切な使用、およびユニットによってトラップされるネガティブな動作を示している可能性があります。多くのソフトウェア開発環境は、開発中の製品を文書化するためにコードだけに依存しているわけではありませんが、単体テストケース自体がこれらの重要な特性を文書化しています。[要出典]

テスト駆動アプローチを使用してソフトウェアを開発する場合、インターフェースを指定するための単体テストの記述と、テストに合格した後に実行されるリファクタリングアクティビティの組み合わせが、正式な設計の代わりになる場合があります。各単体テストは、クラス、メソッド、および観察可能な動作を指定する設計要素と見なすことができます。[要出典]

制限と欠点

テストでは、最も些細なプログラム以外のすべての実行パスを評価できないため、プログラムのすべてのエラーを検出できるわけではありません。この問題は、決定不可能な停止問題スーパーセットですユニットテストについても同じことが言えます。さらに、ユニットテストは、定義上、ユニット自体の機能のみをテストします。したがって、統合エラーやより広範なシステムレベルのエラー(複数のユニットで実行される機能や、パフォーマンスなどの非機能テスト領域など)は検出されません。単体テストは、他のソフトウェアテストと組み合わせて実行する必要があります特定のエラーの有無のみを示すことができるため、アクティビティ。エラーが完全にないことを証明することはできません。すべての実行パスとすべての可能な入力に対して正しい動作を保証し、エラーがないことを保証するには、他の手法、つまり、ソフトウェアコンポーネントに予期しない動作がないことを証明するための形式手法の適用が必要です。[要出典]

単体テストの複雑な階層は、統合テストと同じではありません。周辺機器との統合は統合テストに含める必要がありますが、単体テストには含めないでください。[要出典]統合テストは通常​​、人間による手動テストに大きく依存しています。高レベルまたはグローバルスコープのテストは自動化が難しい場合があり、手動テストは多くの場合、より速く、より安価に見えます。[要出典]

ソフトウェアテストは組み合わせの問題です。たとえば、すべてのブール決定ステートメントには、少なくとも2つのテストが必要です。1つは結果が「true」で、もう1つは結果が「false」です。その結果、記述されたコードのすべての行に対して、プログラマーは3〜5行のテストコードを必要とすることがよくあります。[11]これには明らかに時間がかかり、その投資は努力する価値がないかもしれません。簡単にテストできない問題があります。たとえば、非決定論的であるか、複数のスレッドが関係している問題です。さらに、単体テストのコードは、テストしているコードと同じくらいバグがある可能性があります。人月の神話のフレッド・ブルックスは次のように述べています。[12]つまり、2つのクロノメーターが矛盾している場合、どちらが正しいかをどうやって知ることができますか?

単体テストの作成に関連するもう1つの課題は、現実的で有用なテストを設定することの難しさです。テストされるアプリケーションの一部が完全なシステムの一部のように動作するように、関連する初期条件を作成する必要があります。これらの初期条件が正しく設定されていない場合、テストは現実的なコンテキストでコードを実行しないため、単体テスト結果の値と精度が低下します。[13]

単体テストから意図された利益を得るには、ソフトウェア開発プロセス全体を通して厳格な規律が必要です。実行されたテストだけでなく、このユニットまたはソフトウェア内の他のユニットのソースコードに加えられたすべての変更についても注意深く記録することが不可欠です。バージョン管理システムの使用は不可欠です。ユニットの新しいバージョンが以前に合格した特定のテストに失敗した場合、バージョン管理ソフトウェアは、それ以降にユニットに適用されたソースコードの変更(存在する場合)のリストを提供できます。[要出典]

また、テストケースの失敗を定期的にレビューし、すぐに対処するための持続可能なプロセスを実装することも不可欠です。[14]そのようなプロセスが実装されておらず、チームのワークフローに組み込まれていない場合、アプリケーションは単体テストスイートと同期しなくなり、誤検知が増加し、テストスイートの有効性が低下します。

組み込みシステムソフトウェアの単体テストには、固有の課題があります。ソフトウェアは、最終的に実行されるプラットフォームとは異なるプラットフォームで開発されているため、デスクトッププログラムの場合のように、実際の展開環境でテストプログラムを簡単に実行することはできません。[15]

単体テストは、メソッドに入力パラメーターといくつかの出力がある場合に最も簡単になる傾向があります。メソッドの主な機能がアプリケーションの外部の何かと対話することである場合、単体テストを作成することはそれほど簡単ではありません。たとえば、データベースで機能するメソッドでは、データベースの相互作用のモックアップを作成する必要がある場合があります。これは、実際のデータベースの相互作用ほど包括的ではない可能性があります。[16] [より良い情報源が必要]

これは、実装のいくつかの要素を指定するJavaの一連のテストケースです。まず、Adderと呼ばれるインターフェースと、AdderImplと呼ばれる引数がゼロのコンストラクターを持つ実装クラスが必要です。さらに、Adderインターフェイスには、別の整数を返す2つの整数パラメーターを持つaddというメソッドが必要であると主張します。また、いくつかのテストメソッドの小さな範囲の値に対するこのメソッドの動作を指定します。

import static  org.junit.Assert.assertEquals ;

import  org.junit.Test ;

パブリック クラス TestAdder  {

    @Test 
    public  void  testSumPositiveNumbersOneAndOne () { 
        Adder  adder  =  new  AdderImpl (); 
        assertEquals 2  adder .add 1、1 ; _ } 
    

    //正の数1と2を追加できますか?
    @Test 
    public  void  testSumPositiveNumbersOneAndTwo () { 
        Adder  adder  =  new  AdderImpl (); 
        assertEquals 3  adder。add 1、2 ; _ _ } 
    

    //正の数2と2を追加できますか?
    @Test 
    public  void  testSumPositiveNumbersTwoAndTwo () { 
        Adder  adder  =  new  AdderImpl (); 
        assertEquals 4  adder .add 2、2 ; _ } 
    

    //ゼロニュートラルですか?
    @Test 
    public  void  testSumZeroNeutral () { 
        Adder  adder  =  new  AdderImpl (); 
        assertEquals 0  adder。add 0、0 ; _ _ } 
    

    //負の数-1と-2を追加できますか?
    @Test 
    public  void  testSumNegativeNumbers () { 
        Adder  adder  =  new  AdderImpl (); 
        assertEquals -3 adder。add -1 -2 ; _ _ _ }  
    

    //正と負を追加できますか?
    @Test 
    public  void  testSumPositiveAndNegative () { 
        Adder  adder  =  new  AdderImpl (); 
        assertEquals 0  adder。add -1、1 ; _ _ _ } 
    

    //大きな数字はどうですか?
    @Test 
    public  void  testSumLargeNumbers () { 
        Adder  adder  =  new  AdderImpl (); 
        assertEquals 2222  adder .add 1234、988 ; _ } } 
    

この場合、最初に作成された単体テストは、プログラマーに残されている実装の詳細ではなく、目的のソリューションの形式と動作を指定する設計ドキュメントとして機能します。「おそらく機能する可能性のある最も単純なことを行う」の実践に続いて、テストに合格するための最も簡単な解決策を以下に示します。

interface  Adder  { 
    int  add int  a  int  b ); 
}
クラス AdderImplは Adderを実装し ます{ publicint add int a int b { return a + b ; } } 
          
           
    

実行可能仕様として

ユニットテストを設計仕様として使用することには、他の設計方法に比べて1つの大きな利点があります。設計ドキュメント(ユニットテスト自体)自体を使用して、実装を検証できます。開発者が設計に従ってソリューションを実装しない限り、テストに合格することはありません。

単体テストには、UMLダイアグラムなどの図式仕様のアクセシビリティの一部が欠けていますが、自動化されたツールを使用して単体テストから生成される場合があります。最近のほとんどの言語には無料のツールがあります(通常はIDEの拡張機能として利用できます)。xUnitフレームワークに基づくツールのような無料のツールは、人間が消費するためのビューのグラフィックレンダリングを別のシステムにアウトソーシングします。

アプリケーション

エクストリームプログラミング

ユニットテストは、自動化されたユニットテストフレームワークに依存するエクストリームプログラミングの基礎ですこの自動化された単体テストフレームワークは、サードパーティ(xUnitなど)にすることも、開発グループ内で作成することもできます。

エクストリームプログラミングでは、テスト駆動開発用の単体テストの作成を使用します開発者は、ソフトウェア要件または欠陥のいずれかを明らかにする単体テストを作成します。要件がまだ実装されていないか、既存のコードの欠陥を意図的に公開しているため、このテストは失敗します。次に、開発者は、他のテストとともにテストに合格するための最も単純なコードを記述します。

システム内のほとんどのコードは単体テストされていますが、必ずしもコード内のすべてのパスであるとは限りません。エクストリームプログラミングでは、従来の「すべての実行パスをテストする」方法よりも、「壊れる可能性のあるすべてをテストする」戦略が義務付けられています。これにより、開発者は従来の方法よりも少ないテストを開発することになりますが、これは実際には問題ではなく、事実を言い換えると、すべての実行パスを完全にテストするのに十分な方法で従来の方法を実行することはめったにありません。[要出典]エクストリームプログラミングは、テストが網羅的であることはめったにないことを認識し(多くの場合、費用と時間がかかりすぎて経済的に実行できないため)、限られたリソースに効果的に集中する方法についてのガイダンスを提供します。

重要なことに、テストコードは、すべての重複が削除され、実装コードと同じ品質で維持されるという点で、ファーストクラスのプロジェクトアーティファクトと見なされます。開発者は、単体テストコードを、テストするコードと組み合わせてコードリポジトリにリリースします。エクストリームプログラミングの徹底的な単体テストにより、よりシンプルで信頼性の高いコード開発とリファクタリング、簡素化されたコード統合、正確なドキュメント、よりモジュール化された設計など、上記の利点が得られます。これらの単体テストも、回帰テストの形式として常に実行されます。

ユニットテストは、EmergentDesignの概念にとっても重要です。緊急設計はリファクタリングに大きく依存しているため、単体テストは不可欠なコンポーネントです。[17]

ユニットテストフレームワーク

単体テストフレームワークは、ほとんどの場合、コンパイラスイートの一部として配布されていないサードパーティ製品です。これらは、さまざまな言語用に開発された単体テストのプロセスを簡素化するのに役立ちます

一般に、特定のフレームワークのサポートなしで、テスト対象のユニットを実行し、アサーション例外処理、またはその他の制御フローメカニズムを使用して障害を通知するクライアントコードを記述して、ユニットテストを実行できます。フレームワークのない単体テストは、単体テストを採用するための参入障壁があるという点で価値があります。単体テストが少ない方がまったくないよりはましですが、フレームワークが整ったら、単体テストの追加は比較的簡単になります。[18]一部のフレームワークでは、多くの高度な単体テスト機能が欠落しているか、手動でコーディングする必要があります。

言語レベルの単体テストのサポート

一部のプログラミング言語は、単体テストを直接サポートしています。それらの文法により、ライブラリ(サードパーティまたは標準)をインポートせずに単体テストを直接宣言できます。さらに、単体テストのブール条件は、非ユニットテストコードで使用されるブール式と同じ構文で表すことができます。たとえばifwhileステートメントに使用されるものなどです。

単体テストのサポートが組み込まれている言語は次のとおりです。

単体テストのサポートが組み込まれていない一部の言語には、非常に優れた単体テストライブラリ/フレームワークがあります。それらの言語は次のとおりです。

も参照してください

参考文献

  1. ^ a b Kolawa、Adam; ホイジンガ、ドロタ(2007)。自動欠陥防止:ソフトウェア管理のベストプラクティスWiley-IEEE Computer SocietyPress。p。75. ISBN 978-0-470-04212-0
  2. ^ a b ハミル、ポール(2004)。ユニットテストフレームワーク:高品質のソフトウェア開発のためのツールO'Reilly Media、Inc。ISBN 9780596552817
  3. ^ Xie、Tao「オブジェクト指向プログラムの差分単体テストのフレームワークに向けて」(PDF)2012年7月23日取得
  4. ^ ファウラー、マーティン(2007年1月2日)。「モックはスタブではありません」2008年4月1日取得
  5. ^ 「xUnit.net(デスクトップ)入門」
  6. ^ 「理論」GitHub
  7. ^ 「パラメータ化されたテスト」GitHub
  8. ^ ベーム、バリーW .; Papaccio、Philip N.(1988年10月)。「ソフトウェアコストの理解と管理」(PDF)ソフトウェアエンジニアリングに関するIEEEトランザクション14(10):1462–1477。土井10.1109 /32.6191 2016年5月13日取得
  9. ^ 「早期にそして頻繁にテストする」マイクロソフト。
  10. ^ 「それが機能することを証明する:ソフトウェアのテストと検証のためのユニットテストフレームワークの使用」NationalInstruments2017年8月21日。
  11. ^ Cramblitt、Bob(2007年9月20日)。「アルベルト・サヴォイアはソフトウェアテストを称賛しています」2007年11月29日取得
  12. ^ Brooks、Frederick J.(1995)[1975]。神話の男-月アディソン-ウェスリー。p。 64ISBN 978-0-201-83595-3
  13. ^ Kolawa、Adam(2009年7月1日)。「ユニットテストのベストプラクティス」2012年7月23日取得
  14. ^ daVeiga、灘(2008年2月6日)。「恐れることなくコードを変更する:回帰セーフティネットを利用する」2008年2月8日取得
  15. ^ Kucharski、Marek(2011年11月23日)。「組み込み開発のためのユニットテストの実用化」2020年7月20日取得
  16. ^ http://wiki.c2.com/?UnitTestsAndDatabases
  17. ^ 「アジャイルエマージェントデザイン」アジャイルシェルパ。2010年8月3日。 2012年3月22日のオリジナルからアーカイブ2012年5月8日取得
  18. ^ ブルズアイテストテクノロジー(2006–2008)。「中間カバレッジ目標」2009年3月24日取得
  19. ^ 「クリスタルスペック」crystal-lang.org 2017年9月18日取得
  20. ^ 「ユニットテスト-Dプログラミング言語」Dプログラミング言語D言語財団2017年8月5日取得
  21. ^ 「テスト-Goプログラミング言語」golang.org 2013年12月3日取得
  22. ^ Pythonドキュメント(2016)。「unittest-ユニットテストフレームワーク」2016年4月18日取得
  23. ^ ウェールズ、ノエル; カルペッパー、ライアン。「RackUnit:ユニットテスト」PLT DesignInc 2019年2月26日取得
  24. ^ ウェールズ、ノエル; カルペッパー、ライアン。「RacketメインディストリビューションのRackUnitユニットテストパッケージ部分」PLT DesignInc 2019年2月26日取得
  25. ^ 「ミニテスト(ルビー2.0)」Ruby-Doc.org。
  26. ^ Rustプロジェクト開発者(2011–2014)。「Rustテストガイド(Rust 0.12.0-毎晩)」2014年8月12日取得
  27. ^ シエラ、スチュアート。「clojure.testのAPI-Clojurev1.6(安定版)」2015年2月11日取得
  28. ^ 「ペスターフレームワーク」GitHub2016年1月28日取得

さらに読む

  • フェザーズ、マイケルC.(2005)。レガシーコードを効果的に使用するニュージャージー州アッパーサドルリバー:プレンティスホールプロフェッショナルテクニカルリファレンス。ISBN 978-0131177055

外部リンク