テストは開発の非常に重要な側面であり、アプリケーションの運命を大きく左右します。優れたテストでは、アプリケーションのクラッシュの原因となる問題を早期に発見できますが、テストが不十分な場合は、常に失敗やダウンタイムが発生することがよくあります。
ソフトウェア テストには、単体テスト、機能テスト、統合テストの 3 つの主なタイプがありますが、このブログ投稿では、開発者レベルの単体テストについて説明します。詳細に入る前に、これら 3 つのテストのそれぞれの詳細を確認しましょう。
単体テストは、個々のコードコンポーネントをテストし、コードが期待どおりに動作することを確認するために使用されます。単体テストは開発者によって作成および実行されます。ほとんどの場合、JUnit や TestNG などのテスト フレームワークが使用されます。通常、テスト ケースはメソッド レベルで記述され、自動化によって実行されます。
統合テストでは、システムが全体として機能しているかどうかを確認します。統合テストも開発者によって行われますが、単一のコンポーネントをテストするのではなく、コンポーネント全体をテストするように設計されています。システムは、コード、データベース、Web サーバーなどの多くの個別のコンポーネントで構成されます。統合テストでは、コンポーネントの配線、ネットワーク アクセス、データベースの問題などの問題を明らかにできます。
機能テストでは、指定された入力の結果を仕様と比較することで、各機能が正しく実装されているかどうかをチェックします。通常、これは開発者レベルではありません。機能テストは別のテスト チームによって実行されます。テスト ケースは仕様に基づいて作成され、実際の結果が期待される結果と比較されます。 Selenium や QTP など、自動機能テストに使用できるツールがいくつかあります。
前述したように、単体テストは開発者がコードが適切に動作しているかどうかを判断するのに役立ちます。このブログ投稿では、Java での単体テストに役立つヒントを提供します。
Java には単体テスト用のフレームワークがいくつか用意されています。 TestNG と JUnit は、最も人気のあるテスト フレームワークです。 JUnit と TestNG の優れた機能:
セットアップと実行が簡単。
サポートコメント。
特定のテストを無視またはグループ化して一緒に実行できます。
パラメータ化されたテスト、つまり実行時に異なる値を指定して単体テストを実行することをサポートします。
Ant、Maven、Gradle などのビルド ツールと統合することにより、自動テスト実行をサポートします。
EasyMock は、JUnit や TestNG などの単体テスト フレームワークを補完するモック フレームワークです。 EasyMock 自体は完全なフレームワークではありません。テストを容易にするためにモック オブジェクトを作成する機能が追加されるだけです。たとえば、テストしたいメソッドの 1 つは、データベースからデータを取得する DAO クラスを呼び出すことができます。この場合、EasyMock を使用して、ハードコードされたデータを返す MockDAO を作成できます。これにより、データベースへのアクセスを心配することなく、目的のメソッドを簡単にテストできます。
テスト駆動開発 (TDD) は、コーディングを開始する前に要件に基づいてテストを作成するソフトウェア開発プロセスです。まだコーディングが行われていないため、テストは最初は失敗します。次に、テストに合格するための最小限のコードを記述します。次に、最適化されるまでコードをリファクタリングします。
目標は、要件さえ満たさない可能性のあるコードを最初から書くのではなく、すべての要件をカバーするテストを書くことです。 TDD が優れているのは、保守が容易なシンプルな モジュラー コードが得られるからです。全体的な開発速度が向上し、欠陥が見つけやすくなります。さらに、単体テストは TDD アプローチの副産物として作成されます。
ただし、TDD はすべての状況に適しているわけではありません。複雑な設計のプロジェクトでは、先を考えずにテスト ケースを通過しやすくするために最も単純な設計に重点を置くと、コードが大幅に変更される可能性があります。さらに、TDD 手法は、レガシー システム、GUI アプリケーション、データベースと連携するアプリケーションと対話するシステムには使用が困難です。さらに、コードの変更に応じてテストを更新する必要があります。
したがって、TDD アプローチの採用を決定する前に、上記の要素を考慮し、プロジェクトの性質に応じて対策を講じる必要があります。
コード カバレッジは、単体テストの実行時に実行されるコードの量を測定します (パーセントで表されます)。一般に、カバレッジが高いコードは、テスト中により多くのソース コードが実行されるため、検出されないバグが含まれる可能性が低くなります。コード カバレッジを測定するためのベスト プラクティスには次のようなものがあります。
Clover、Corbetura、JaCoCo、Sonar などのコード カバレッジ ツールを使用します。ツールを使用すると、テストされていないコードの領域を指摘し、それらの領域をカバーする追加のテストを開発できるため、テストの品質を向上させることができます。
新しい機能が作成されるたびに、すぐに新しいテスト カバレッジを作成します。
コードのすべての分岐、つまり if/else ステートメントをカバーするテスト ケースがあることを確認してください。
コード カバレッジが高いからといって、完璧なテストが保証されるわけではないので、注意してください。
以下の concat メソッドは入力としてブール値を受け入れ、ブール値が true の場合にのみ 2 つの strings を渡します:
public String concat(boolean append, String a,String b) { String result = null; If (append) { result = a + b; } return result.toLowerCase(); }
上記のメソッドのテスト ケースは次のとおりです:
@Test public void testStringUtil() { String result = stringUtil.concat(true, "Hello ", "World"); System.out.println("Result is "+result); }
この場合、値を実行します。テストの結果は真実です。テストが実行されると、テストは成功します。コード カバレッジ ツールを実行すると、concat メソッド内のすべてのコードが実行されたため、100% のコード カバレッジが表示されます。ただし、値 false でテストを実行すると、NullPointerException がスローされます。したがって、100% のコード カバレッジは、実際にはテストがすべてのシナリオをカバーしていることを意味するわけではなく、テストが良好であることを意味するわけでもありません。
JUnit4 が登場する前は、実行するテスト ケースのデータをテスト ケースにハードコーディングする必要がありました。その結果、異なるデータでテストを実行するにはテスト ケース コードを変更する必要があるという制限が生じました。ただし、JUnit4 と TestNG はテスト データの外部化をサポートしているため、ソース コードを変更せずに、異なる データセット に対してテスト ケースを実行できます。
次の MathChecker クラスには、数値が奇数かどうかをチェックするメソッドがあります:
public class MathChecker { public Boolean isOdd(int n) { if (n%2 != 0) { return true; } else { return false; } } }
以下は、MathChecker クラスの TestNG テスト ケースです:
public class MathCheckerTest { private MathChecker checker; @BeforeMethod public void beforeMethod() { checker = new MathChecker(); } @Test @Parameters("num") public void isOdd(int num) { System.out.println("Running test for "+num); Boolean result = checker.isOdd(num); Assert.assertEquals(result, new Boolean(true)); } }
以下は、testng.xml (TestNG の構成ファイル) です。 、テストを実行するデータがあります:
<?xml version="1.0" encoding="UTF-8"?> <suite name="ParameterExampleSuite" parallel="false"> <test name="MathCheckerTest"> <classes> <parameter name="num" value="3"></parameter> <class name="com.stormpath.demo.MathCheckerTest"/> </classes> </test> <test name="MathCheckerTest1"> <classes> <parameter name="num" value="7"></parameter> <class name="com.stormpath.demo.MathCheckerTest"/> </classes> </test> </suite>
見てわかるように、この場合、テストは値 3 と 7 に対して 1 回ずつ、計 2 回実行されます。 XML 構成ファイルを介してテスト データを指定するだけでなく、DataProvider アノテーションを介してクラスでテスト データを提供することもできます。
TestNG と同様に、テスト データも JUnit 用に外部化できます。以下は、上記と同じ MathChecker クラスの JUnit テスト ケースです:
@RunWith(Parameterized.class) public class MathCheckerTest { private int inputNumber; private Boolean expected; private MathChecker mathChecker; @Before public void setup(){ mathChecker = new MathChecker(); } // Inject via constructor public MathCheckerTest(int inputNumber, Boolean expected) { this.inputNumber = inputNumber; this.expected = expected; } @Parameterized.Parameters public static Collection<Object[]> getTestData() { return Arrays.asList(new Object[][]{ {1, true}, {2, false}, {3, true}, {4, false}, {5, true} }); } @Test public void testisOdd() { System.out.println("Running test for:"+inputNumber); assertEquals(mathChecker.isOdd(inputNumber), expected); } }
見てわかるように、テストが実行されるテスト データは getTestData() メソッドによって指定されます。このメソッドは、データをハードコーディングする代わりに外部ファイルからデータを読み取るように簡単に変更できます。
多くの初心者開発者は、コードが正しく実行されるかどうかを確認するために、コードの各行の後に System.out.println ステートメントを記述することに慣れています。この手法は単体テストにも適用されることが多く、その結果、テスト コードが乱雑になってしまいます。混乱が生じるだけでなく、コンソールに出力された出力を確認してテストが正常に実行されたかどうかを確認するために開発者が手動で介入する必要があります。より良いアプローチは、テスト結果を自動的に示すアサーションを使用することです。
以下の StringUti クラスは、2 つの 入力文字を連結し、結果を返すメソッドを持つ単純なクラスです:
public class StringUtil { public String concat(String a,String b) { return a + b; } }
上記のメソッドに対する 2 つの単体テストを次に示します:
@Test public void testStringUtil_Bad() { String result = stringUtil.concat("Hello ", "World"); System.out.println("Result is "+result); } @Test public void testStringUtil_Good() { String result = stringUtil.concat("Hello ", "World"); assertEquals("Hello World", result); }
testStringUtil_Bad は常にパスされるため、主張はありません。開発者は、コンソール上のテスト出力を手動で確認する必要があります。 testStringUtil_Good は、メソッドが間違った結果を返し、開発者の介入を必要としない場合には失敗します。
一部のメソッドには決定的な結果がありません。つまり、メソッドの出力は事前に不明であり、毎回変わる可能性があります。たとえば、次のコードを考えてみます。これには、複雑な function と、その複雑な関数の実行にかかる時間をミリ秒単位で計算するメソッドが含まれています。
public class DemoLogic { private void veryComplexFunction(){ //This is a complex function that has a lot of database access and is time consuming //To demo this method, I am going to add a Thread.sleep for a random number of milliseconds try { int time = (int) (Math.random()*100); Thread.sleep(time); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } public long calculateTime(){ long time = 0; long before = System.currentTimeMillis(); veryComplexFunction(); long after = System.currentTimeMillis(); time = after - before; return time; } }
この場合、calculateTime メソッドが実行されるたびに、次のコードが返されます。異なる値。このメソッドの出力は可変であるため、このメソッドのテスト ケースを作成しても役に立ちません。したがって、テスト メソッドでは特定の実行の出力を検証できません。
通常、開発者は、アプリケーションが期待どおりに動作することを確認するために、テスト ケースの作成に多くの時間と労力を費やします。ただし、陰性のテストケースをテストすることも重要です。ネガティブ テスト ケースは、システムが無効なデータを処理できるかどうかをテストするテスト ケースを指します。たとえば、ユーザーが入力した長さ 8 の英数字値を読み取る単純な関数を考えてみましょう。英数字の値に加えて、次のネガティブ テスト ケースもテストする必要があります:
ユーザー指定の特殊文字などの英数字以外の値。
ユーザー指定の null 値。
8 文字より大きいまたは小さいユーザー指定の値。
同様に、境界テスト ケースでは、システムが極値に対して機能するかどうかをテストします。たとえば、ユーザーが 1 から 100 までの数値を入力したい場合、1 と 100 が境界値であり、これらの値についてシステムをテストすることが非常に重要です。
以上がJava 単体テストの 7 つのヒントの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。