「なぜ単体テストを書いていないのか」という質問に対する最も一般的な答えの1つは、「誰が私のテストのためにテストを書くのか」という質問です。 私のテストにエラーがないという保証はどこにありますか?」、これは単体テストの本質に対する重大な誤解を示しています。
この記事の目的は、この点を簡潔かつ明確に把握して、これ以上意見の相違がないようにすることです。
ユニットテストは
プログラム入力の結果と出力結果を示すいくつかの例のセット。 たとえば、プログラムが斜辺の長さを計算する場合、単体テストでは1行で十分です。
assertEquals(5, hypotenuse(3, 4));
これらのテスト値を自分で考え出す必要があります。一枚の紙または電卓の列で数えます。
もちろん、このテストで十分かどうかはあなた次第です。 特定のタスクで、関数が非常に小さな数で正しく動作することが非常に重要な場合は、別のテストを追加する必要があります assertEquals(0.00005, hypotenuse(0.00003, 0.00004));
また、負のパラメータまたは他のパラメータの場合の関数の動作を記述することも重要です。 一般的には決定しますが、主なアイデアは、単体テストにいくつかの重要な例があるはずです。
ルール
そのため、単体テストは次の点でプログラムと異なります。
- テスト対象のプログラムよりもはるかに簡単です。
- 例のセットのみがあります
- プログラム内にあるロジック はありません (自分でサンプルを作成します)
- 単体テストでは、考えられるすべてのケースをカバーすることはできず、カバーすべきではありません。 むしろ、すべての重要なケース 、つまり個別に議論すべきケースを示す必要があります。
- 単体テストは、プログラムのドキュメントとして機能する必要があります。つまり、単体テストを読んだ後、プログラムの仕組みを理解する必要があります。 通常、この目的のためにテストメソッドの名前が使用されます。
最初の段落は、「私のテストのために誰がテストを書くのか?」という質問に対する答えです。 単体テストはプログラムよりも桁違いに単純なので、単体テストは桁違いに簡単に書く必要がありますが、とにかく非常に単純なので、ほとんど不可能です。
2番目の質問「テストでエラーが発生しないという保証はどこにありますか?」に対する答えは、これです。保証はありません。 しかし、テストで規定された「正しい」解答がプログラムで与えられた解答とは
異なる方法で受け取られたという事実により、両方の方法が正しいことをより確信することができます。
UPD:
kalantyrは、ユニットテストとコードは相互に接続されているため互いにテストすることを示しています。 つまり、ある意味では、テストのテストはテストされたコードそのものです。
例
上記のすべてを説明する単体テストの最小限の例をここで示してみましょう。 ある年がうるう年かどうかを判断する関数を作成する必要があるとします。 うるう年とは、100で除算されているが400で除算されていない年を除き、4で除算された年であることを思い出させてください。
public class LeapYear { public static boolean isLeap(int year) { return year % 4 == 0 && year % 100 != 0 || year % 400 == 0; } } import org.junit.Test; import static junit.framework.Assert.*; public class LeapYearTest { @Test public void yearDividing4IsLeap() throws Exception { assertTrue(isLeap(2008)); assertTrue(isLeap(2012)); assertFalse(isLeap(2011)); } @Test public void exceptYearDividing100WhichIsNotLeap() throws Exception { assertFalse(isLeap(1900)); assertFalse(isLeap(2100)); } @Test public void exceptYearDividing400WhichIsLeap() throws Exception { assertTrue(isLeap(1600)); assertTrue(isLeap(2000)); } }
ご覧のとおり、テストケース自体の中には、具体的な簡単な例があります。2000、2008、1011。私は自分で頭から発明しました。 また、テストケースの名前は、それらを人間の言語で説明(要約)します。 1対1のテスト方法の名前は、上記の「うるう年」という用語の説明と同じであることに注意してください。 そのため、テストはドキュメントとして読む必要があります。 そして、これは例えばIDEAでの見た目です:

間違い
ユニットテストの本質を誤解しているため、ユニットテストを書くときに大きな間違いがしばしば発生し、ユニットテストが役に立たず、時間がかかり、ユニットテストが必要かどうかについて無限の議論につながります。
最初のよくある間違いは、何もチェックしないことです。 例:
@Test public void testLeapYear() throws Exception { int year = 2004; assertEquals(2004, year); }
この単体テストは、プログラムをテストしないため、意味がありません。 彼がチェックする最大のことは、Javaが正しく動作することですが、これは妄想です。 Oracleにこれを行わせてください。
明らかな不条理にもかかわらず、最初にJUnitを使用するすべての人が開始するのはこのようなテストです。
2番目の典型的な間違いは、プログラム内のテストで同じロジックを繰り返すことです。 例:
@Test public void testLeapYear() throws Exception { for (int i=0; i<100000; i++) { assertEquals(i % 4 == 0 && i % 100 != 0 || i % 400 == 0, isLeap(i)); } }
この単体テストは、プログラマーがコード自体と同じ間違いを犯す可能性があるというまさに理由で悪いです。
臭い
単体テストが正しくないことを示すサイン:
- 条件とサイクル
単体テストで条件とサイクルが表示される場合、これはテストが正しくないことの明確な兆候です。 ループを取り除き、いくつかの簡単な例を書いてみてください。
もちろん、テストでは、たとえば2つの配列を比較するためにループが必要になる場合があります。 むしろ、テスト対象のプログラムと同じロジックを持つサイクルが存在しないことを意味していました。 - テストメソッドの名前は何が機能するかを示していません。
タイトルにテスト対象の名前(testLeapYearなど)のみが表示される場合は、注意してください。 彼はおそらく何もテストしていないか、あまりテストしていません。 テストメソッドの正しい名前は、次のように聞こえるはずです。「そのような状況では、そのような方法はそのような方法で動作するはずです」 - テストメソッドの本文にアサートが多すぎます。
このようなテストメソッドは、あまりにも多くの側面をチェックします。 壊れた場合、メソッドの名前によって、エラーが何であるかをすぐに判断することはできません。テストクラスのコードを分析する必要があります。 テストメソッドをいくつかのメソッドに分割し、名前を話すようにしてください。
もちろん、たとえば、必要なすべての値が大きなHTMLに存在することを確認するために、テストメソッドに多くのアサートがある場合があります。 むしろ、この推奨事項は、「テストを短縮し、1つの方法で1つの可能なパスをチェックすることで、テストが落ちた場所を簡単に特定できる」と理解する必要があります。 - 他にどんな兆候を知っていますか?..
オープンソース
有名なオープンソースプロジェクトの単体テストを研究することは常に役立ちます。 このトピックは別の記事に値しますが、最初に頭に浮かんだのは
Apache Velocityと
Spring Frameworkです。 最初の
UnicodeInputStreamTestCaseプロジェクトのテストは好きではありませんでした。テストメソッドの名前は実際にはプログラムの動作を説明していないためです(たとえば、「testSimpleStream()」)。 しかし、たとえばSpringのテストが好きでした。たとえば、
CollectionUtilsクラスには
CollectionUtilsTests単体テストがあり、
Assertクラスには
AssertTests単体テストがあります。
この記事が、単体テストが役に立たず、時間がかかるなどという永遠の議論を止め、書く方法、書く量、書くものなどに関するさらなる議論の出発点として役立つことを願っています。 そして、誰かがハブから
脱出し 、「
Test Driven Development(Kent Beck) 」、「
xUnit Test Patterns(Gerard Meszaros) 」、私のお気に入りの「
Clean Code(Robert C. Martin) ) 」。
テストするかしないか-それは質問ではありません...