単一の単体テストで複数のアサートを持つことは問題ありませんか?


397

このすばらしい投稿へのコメントで、Roy Osherove は、単一のテストで各アサートを実行するように設計されたOAPTプロジェクトについて言及しました。

以下は、プロジェクトのホームページに書かれています。

適切な単体テストは1つの理由で失敗するはずです。そのため、単体テストごとに1つのアサートを使用する必要があります。

また、ロイはコメントで次のように書いています。

私のガイドラインは、通常、テストごとに1つの論理CONCEPTをテストすることです。同じオブジェクトに複数のアサートを設定でき ます。通常、それらはテストされる同じ概念になります。

複数のアサーションが必要な場合もあると思います(例:Guard Assertion)が、一般的にはこれを回避しようとします。あなたの意見は何ですか?複数のアサートが本当に必要な実世界の例を提供してください。


2
複数のアサーションを持たずにどのようにモックをしますか?モックに対する期待は、それ自体がアサーションであり、あなたが課す呼び出しの順序を含みます。
クリストファー・クロイツィヒ

15
私は過去にメソッドごとに1つのアサートの哲学が悪用されているのを見てきました。古い同僚は、これを可能にするためにファンキーな継承メカニズムを使用していました。多くのサブクラス(ブランチごとに1つ)と、さまざまな結果をチェックするためだけに同じセットアップ/ティアダウンプロセスを実行する多くのテストが行​​われました。遅く、読みづらく、重大なメンテナンスの問題がありました。私は彼に、より古典的なアプローチに戻るように説得しませんでした。Gerard Meszarosの本は、このトピックについて詳しく説明しています。
トラビスパーク

3
一般的な経験則として、テストごとのアサートの数を最小限に抑えるようにしてください。ただし、テストによって問題がコード内の特定の場所に十分に絞り込まれている限り、有用なテストになります。
ConditionRacer

2
さまざまなエッジケースの動作をテストするために、RowTest(MbUnit)/ TestCase(NUnit)の代わりに複数のアサートが使用されるケースを見てきました。仕事に適したツールを使用してください!(残念ながら、MSTestにはまだ行テスト機能がないようです。)
GalacticCowboy

@GalacticCowboy テストデータソースRowTestと同様の機能をTestCase使用できます。シンプルなCSVファイルを使用していますが、非常に成功しています。
julealgon

回答:


236

必ずしも悪いことだとは思いませんが、テストでは単一のアサートのみを持つように努力すべきだと思います。これは、より多くのテストを書くことを意味し、私たちのテストは一度に1つのことだけをテストすることになります。

そうは言っても、テストの半分は実際には1つのアサートしか持っていないと言えるでしょう。テストで約5つ以上のアサートがある場合にのみ、コード(テスト?)のにおいになると思います。

複数のアサートをどのように解決しますか?


9
この回答のように-そのOK、すなわち、そのではない、一般的な例では、良い:(
Murph

5
え?どうしてそうするか?メソッドの実行はまったく同じですか?
jgauffin

197
ユニットテストごとに1つのアサートは、読者が上下にスクロールする能力をテストするための優れた方法です。
トム

3
この背後にある理由は何ですか?現状では、この現在の答えは、それがどうあるべきかを述べているだけであり、なぜではない。
スティーブンジュリス

37
強く同意しない。答えには、単一のアサートを持つことの利点はリストされていませんが、明らかな欠点は、インターネット上の答えがそう言うので、テストをコピーアンドペーストするだけでよいことです。結果の複数のフィールドまたは単一の操作の複数の結果をテストする必要がある場合は、大きなblob-assertでテストするよりもはるかに有用な情報を提供するため、すべてを独立したアサートで絶対にアサートする必要があります。適切なテストでは、操作の単一の結果ではなく、単一の操作をテストします。
ピーター

296

テストは1つの理由でのみ失敗するはずですが、それは常に1つのAssertステートメントのみが存在することを意味するわけではありません。私見では、「アレンジ、アクト、アサート」パターンを保持することがより重要です。

重要なのは、アクションが1つしかないこと、そしてアサートを使用してそのアクションの結果を検査することです。ただし、「アレンジ、アクト、アサート、テスト終了」です。別のアクションを実行してテストを続行し、その後さらにアサートする場合は、代わりに別のテストを作成します。

同じアクションのテストの一部を形成する複数のアサートステートメントを見ることができてうれしいです。例えば

[Test]
public void ValueIsInRange()
{
  int value = GetValueToTest();

  Assert.That(value, Is.GreaterThan(10), "value is too small");
  Assert.That(value, Is.LessThan(100), "value is too large");
} 

または

[Test]
public void ListContainsOneValue()
{
  var list = GetListOf(1);

  Assert.That(list, Is.Not.Null, "List is null");
  Assert.That(list.Count, Is.EqualTo(1), "Should have one item in list");
  Assert.That(list[0], Is.Not.Null, "Item is null");
} 

これらを1つのアサートに結合することもできます、それは、すべきである、またはする必要があると主張することと異なります。それらを組み合わせても改善はありません。

例えば最初のものは可能性があること

Assert.IsTrue((10 < value) && (value < 100), "Value out of range"); 

しかし、これは良くありません-それからのエラーメッセージはそれほど具体的ではなく、他の利点はありません。2つまたは3つ(またはそれ以上)のアサートを1つの大きなブール条件に組み合わせると、読みにくく、変更しにくく、失敗した理由を突き止めるのが難しい他の例を考えることができると確信しています。なぜルールのためだけにこれを行うのですか?

NB:ここで書いているコードはNUnitを使用したC#ですが、原則は他の言語とフレームワークでも同じです。構文も非常に似ている場合があります。


32
重要なのは、アクションが1つしかないこと、そしてアサートを使用してそのアクションの結果を検査することです。
アミターバ

1
Arrangeに時間がかかる場合、複数のAssertを持つことも興味深いと思います。
レクシーノ

1
@ Rekshino、arrangeに時間がかかる場合、たとえば、アレンジコードをテスト初期化ルーチンに入れることで、アレンジコードを共有できます。
ショーンルッティン

2
したがって、Jacoの回答と比較すると、「唯一のアサート」は「アサートの1つのグループのみ」になり、私にとってより意味があります。
ウォルフラット

2
これは良い答えですが、私は単一の主張が良くないことに同意しません。悪いアサートの例は良くはありませんが、それが正しいアサーションだと単一のアサートが良くないという意味ではありません。多くのライブラリはカスタムのアサート/マッチャーを許可しているため、まだ存在しない場合は何かを作成できます。たとえば、私の意見でAssert.IsBetween(10, 100, value)、印刷物Expected 8 to be between 10 and 100 2つの別々の主張よりも優れています。確かにそれは必要ではないと主張することができますが、通常は、それらのセット全体を作成する前に単一のアサートに還元するのが簡単かどうかを検討する価値があります。
Thor84no

85

複数の主張が悪いことだと思ったことは一度もありません。

私はいつもやる:

public void ToPredicateTest()
{
    ResultField rf = new ResultField(ResultFieldType.Measurement, "name", 100);
    Predicate<ResultField> p = (new ConditionBuilder()).LessThanConst(400)
                                                       .Or()
                                                       .OpenParenthesis()
                                                       .GreaterThanConst(500)
                                                       .And()
                                                       .LessThanConst(1000)
                                                       .And().Not()
                                                       .EqualsConst(666)
                                                       .CloseParenthesis()
                                                       .ToPredicate();
    Assert.IsTrue(p(ResultField.FillResult(rf, 399)));
    Assert.IsTrue(p(ResultField.FillResult(rf, 567)));
    Assert.IsFalse(p(ResultField.FillResult(rf, 400)));
    Assert.IsFalse(p(ResultField.FillResult(rf, 666)));
    Assert.IsFalse(p(ResultField.FillResult(rf, 1001)));

    Predicate<ResultField> p2 = (new ConditionBuilder()).EqualsConst(true).ToPredicate();

    Assert.IsTrue(p2(new ResultField(ResultFieldType.Confirmation, "Is True", true)));
    Assert.IsFalse(p2(new ResultField(ResultFieldType.Confirmation, "Is False", false)));
}

ここでは、複数のアサートを使用して、複雑な条件を予想される述語に変換できるようにします。

私は1つのユニット(ToPredicateメソッド)のみをテストしていますが、テストで考えられるすべてをカバーしています。


46
エラー検出のため、複数のアサートは不良です。最初のAssert.IsTrueに失敗した場合、他のアサートは実行されず、それらから情報を取得できません。あなたが何かの役に立つを得ることができアサート5つのテストの代わりに、5と1を持っていた場合は一方
スライ

6
すべてのアサートが同じ種類の機能をテストする場合、それはまだ悪いと思いますか?上記のように、この例は条件をテストし、これのいずれかが失敗した場合、修正する必要があります。以前のアサートが失敗した場合、最後の2つのアサートを見逃す可能性があることは重要ですか?
クリンジ

106
私は自分の問題を一つずつ修正します。したがって、テストが複数回失敗する可能性があるという事実は、私を悩ませません。それらを分割すると、同じエラーが発生しますが、すべて同時に発生します。一度に1つずつ問題を修正する方が簡単だと思います。この場合、最後の2つのアサートはおそらく独自のテストにリファクタリングできることを認めます。
マットエレン

19
-あなたの場合は、NUnitのは、追加の属性のTestCaseを持っている理由です、非常に代表的なものであるnunit.org/?p=testCase&r=2.5
Restuta

10
google C ++テストフレームワークにはASSERT()およびEXPECT()があります。ASSERT()は失敗時に停止し、EXPECT()は続行します。これは、テストで複数のことを検証する場合に非常に便利です。
-ratkok

21

ユニットテストを使用して高レベルの動作を検証する場合、複数のアサーションを1つのテストに絶対に入れます。これは、緊急通知コードに実際に使用しているテストです。テストの前に実行されるコードにより、システムは、メインプロセッサが実行されるとアラームが送信される状態になります。

@Test
public void testAlarmSent() {
    assertAllUnitsAvailable();
    assertNewAlarmMessages(0);

    pulseMainProcessor();

    assertAllUnitsAlerting();
    assertAllNotificationsSent();
    assertAllNotificationsUnclosed();
    assertNewAlarmMessages(1);
}

これは、コードが期待どおりに動作していることを確信するために、プロセスのすべてのステップで存在する必要がある条件を表しています。単一のアサーションが失敗しても、残りのアサーションが実行されないことは気にしません。システムの状態が有効でなくなっているので、それらのその後のアサーションは私に貴重な何かを教えてくれません。*場合は、assertAllUnitsAlerting()失敗した、そして私はの作るために知っているんではないでしょうassertAllNotificationSent()、私は前に、エラーの原因となったものを決定するまで、成功または失敗の修正しました。

(*-わかりました、おそらく問題のデバッグに役立つかもしれません。しかし、テストが失敗したという最も重要な情報はすでに受信されています。)


その場合、依存テストを含むテストフレームワークを使用する方が良いでしょう(たとえば、testngはこの機能をサポートしています)
Kemoda

8
コードが何をしているのか、状態が変化するのを確信できるように、私もこのようなテストを書きます。これは単体テストではなく、統合テストだと思います。
mdma

assertAlarmStatus(int numberOfAlarmMessages);にリファクタリングすることについてのあなたの意見は何ですか?
ボルジャブ

1
あなたの主張は非常に良いテスト名になります。
ビヨンティップリング

無効な入力であっても、テストを実行することをお勧めします。この方法でより多くの情報が得られます(特に、予期しないときにパスした場合)。
CurtainDog

8

1つのメソッドで複数のアサートを行うことは悪いことではないと思うもう1つの理由は、次のコードで説明されています。

class Service {
    Result process();
}

class Result {
    Inner inner;
}

class Inner {
    int number;
}

私のテストでservice.process()は、Innerクラスインスタンスで正しい数を返すテストだけを行います。

テストする代わりに...

@Test
public void test() {
    Result res = service.process();
    if ( res != null && res.getInner() != null ) Assert.assertEquals( ..., res.getInner() );
}

私がやっている

@Test
public void test() {
    Result res = service.process();
    Assert.notNull(res);
    Assert.notNull(res.getInner());
    Assert.assertEquals( ..., res.getInner() );
}

2
それは良いことです。テストには条件付きロジックを使用しないでください。テストがより複雑で読みにくくなります。そして、Royは1つのオブジェクトに対する複数のアサートがほとんどの場合大丈夫であるという彼のブログ投稿でそれを説明したと思います。したがって、あなたが持っているものは単なるガードアサートであり、それらを持っていても大丈夫です。
-Restuta

2
あなたのAssert.notNullsが重複している彼らがnullなら、あなたのテストは、NPEで失敗します。
サラ

1
また、最初の例(のif場合)は次の場合に合格しresますnull
サラ

1
@kai notNullは冗長です、私は同意しますが、例外の代わりにアサート(そして適切なメッセージでも
怠laで

1
失敗したアサーションも例外をスローします。どちらの場合も、スタックトレースを伴ってそれをスローした正確な行への直接リンクを取得します。Assert.assertEquals(..., service.process().getInner());行が「長すぎる」場合に抽出された変数で可能なoneliner ala laを好む
サラ

6

テストは1つの理由でのみ失敗するというルール内で、複数のアサートの記述が有効であるケースがたくさんあると思います。

たとえば、日付文字列を解析する関数を想像してください。

function testParseValidDateYMD() {
    var date = Date.parse("2016-01-02");

    Assert.That(date.Year).Equals(2016);
    Assert.That(date.Month).Equals(1);
    Assert.That(date.Day).Equals(0);
}

テストが失敗した場合、それは1つの理由が原因であり、解析は正しくありません。このテストが3つの異なる理由で失敗する可能性があると主張する場合は、「1つの理由」の定義で私見が細かすぎます。


3

[Test]メソッド自体の内部に複数のアサーションを持つことをお勧めする状況は知りません。人々が複数のアサーションを持つことを好む主な理由は、テスト対象のクラスごとに1つの[TestFixture]クラスを取得しようとしていることです。代わりに、テストをさらに[TestFixture]クラスに分割できます。これにより、最初のアサーションが失敗したものだけでなく、コードが予期した方法で反応しなかった可能性のある複数の方法を確認できます。これを実現する方法は、テスト対象のクラスごとに少なくとも1つのディレクトリがあり、内部に多数の[TestFixture]クラスがあることです。各[TestFixture]クラスは、テストするオブジェクトの特定の状態に基づいて名前が付けられます。[SetUp]メソッドは、オブジェクトをクラス名で記述された状態にします。次に、複数の[Test]メソッドを使用して、オブジェクトの現在の状態に応じて、それぞれが真であると予想されるさまざまなことをアサートします。各[Test]メソッドは、おそらく英語のコードの読み取りではなく、コンセプトに基づいて名前が付けられる場合を除いて、アサートしているものにちなんで名前が付けられます。次に、各[Test]メソッドの実装に必要なのは、何かをアサートしている1行のコードだけです。このアプローチのもう1つの利点は、テスト対象が非常に明確になり、クラス名とメソッド名を見るだけで何が期待できるかが明確になるため、テストが非常に読みやすくなることです。また、テストしたいすべての小さなエッジケースを認識し始め、バグを発見するにつれて、これはより良いスケールになります。おそらく、コードの英語の読み上げではなく、コンセプトにちなんで命名される可能性があります。次に、各[Test]メソッドの実装に必要なのは、何かをアサートしている1行のコードだけです。このアプローチのもう1つの利点は、テスト対象が非常に明確になり、クラス名とメソッド名を見るだけで何が期待できるかが明確になるため、テストが非常に読みやすくなることです。また、テストしたいすべての小さなエッジケースを認識し始め、バグを発見するにつれて、これはより良いスケールになります。おそらく、コードの英語の読み上げではなく、コンセプトにちなんで命名される可能性があります。次に、各[Test]メソッドの実装に必要なのは、何かをアサートしている1行のコードだけです。このアプローチのもう1つの利点は、テスト対象が非常に明確になり、クラス名とメソッド名を見るだけで何が期待できるかが明確になるため、テストが非常に読みやすくなることです。また、テストしたいすべての小さなエッジケースを認識し始め、バグを発見するにつれて、これはより良いスケールになります。そして、クラス名とメソッド名を見るだけであなたが期待するもの。また、テストしたいすべての小さなエッジケースを認識し始め、バグを発見するにつれて、これはより良いスケールになります。そして、クラス名とメソッド名を見るだけであなたが期待するもの。また、テストしたいすべての小さなエッジケースを認識し始め、バグを発見するにつれて、これはより良いスケールになります。

通常、これは、[SetUp]メソッド内のコードの最終行がプロパティ値または戻り値を[TestFixture]のプライベートインスタンス変数に格納することを意味します。次に、異なる[Test]メソッドからこのインスタンス変数について複数の異なることをアサートします。また、テスト対象オブジェクトのさまざまなプロパティが現在望ましい状態に設定されていることについてアサーションを行うこともできます。

オブジェクトを目的の状態にする前に混乱していないことを確認するために、テスト中のオブジェクトを目的の状態にするときに、途中でアサーションを作成する必要がある場合があります。その場合、これらの追加のアサーションは[SetUp]メソッド内に表示されます。[SetUp]メソッド内で何か問題が発生した場合、オブジェクトがテストする予定の状態になる前に、テストに問題があることが明らかになります。

発生する可能性がある他の問題の1つは、スローされると予想される例外をテストしている可能性があることです。これは、上記のモデルに従わないように誘惑することがあります。ただし、[SetUp]メソッド内で例外をキャッチし、それをインスタンス変数に格納することで、引き続き実現できます。これにより、例外についてさまざまなことを表明でき、それぞれが独自の[Test]メソッドになります。また、テスト中のオブジェクトについて他のことをアサートして、スローされている例外による意図しない副作用がないことを確認できます。

例(これは複数のファイルに分割されます):

namespace Tests.AcctTests
{
    [TestFixture]
    public class no_events
    {
        private Acct _acct;

        [SetUp]
        public void SetUp() {
            _acct = new Acct();
        }

        [Test]
        public void balance_0() {
            Assert.That(_acct.Balance, Is.EqualTo(0m));
        }
    }

    [TestFixture]
    public class try_withdraw_0
    {
        private Acct _acct;
        private List<string> _problems;

        [SetUp]
        public void SetUp() {
            _acct = new Acct();
            Assert.That(_acct.Balance, Is.EqualTo(0));
            _problems = _acct.Withdraw(0m);
        }

        [Test]
        public void has_problem() {
            Assert.That(_problems, Is.EquivalentTo(new string[] { "Withdraw amount must be greater than zero." }));
        }

        [Test]
        public void balance_not_changed() {
            Assert.That(_acct.Balance, Is.EqualTo(0m));
        }
    }

    [TestFixture]
    public class try_withdraw_negative
    {
        private Acct _acct;
        private List<string> _problems;

        [SetUp]
        public void SetUp() {
            _acct = new Acct();
            Assert.That(_acct.Balance, Is.EqualTo(0));
            _problems = _acct.Withdraw(-0.01m);
        }

        [Test]
        public void has_problem() {
            Assert.That(_problems, Is.EquivalentTo(new string[] { "Withdraw amount must be greater than zero." }));
        }

        [Test]
        public void balance_not_changed() {
            Assert.That(_acct.Balance, Is.EqualTo(0m));
        }
    }
}

この場合、TestCaseの入力はどのように処理しますか?
サイモンギルビー

したがって、私の現在の組織では、表示されているものと非常によく似た20,000以上の単体テストがあります。これは悪夢です。テストセットアップコードの多くはコピー/貼り付けられ、テストセットアップが正しくなくなり、無効なテストが合格します。[Test]メソッドごとに、そのクラスが再インスタンス化[SetUp]され、メソッドが再度実行されます。これにより、.NETガベージコレクターが強制終了され、テストの実行が非常に遅くなります:ローカルで5分以上、ビルドサーバーで20分以上。20Kテストは約2〜3分で実行されます。特に大規模なテストスイートの場合、このテストスタイルはまったくお勧めしません
-4つの過去の深夜

@fourpastmidnightあなたが言ったことのほとんどは正当な批判のように見えますが、セットアップコードをコピーして貼り付けて間違っているという点は、構造の問題ではなく、無責任なプログラマーの問題です(無責任なマネージャーまたは否定的な環境の結果である可能性があります悪いプログラマーよりも)。人々が単にコードをコピーして貼り付け、それが正しいことを望み、何らかの理由で何らかの理由でコードを理解することを気にしない場合、これを行わないように訓練するか、できない場合は手放す必要があります訓練された。それはプログラミングのすべての優れた原則に反します。
still_dreaming_1

しかし、一般的には、これはクレイジーなやり過ぎであり、あらゆる種類の問題につながる大量の膨張/手荷物/複製をもたらします。私は以前は夢中になっていて、このようなものをお勧めしました。それは、前日に自分自身について毎日言うことができることを望んでいるからです。それは、私が物事のより良い方法を見つけることを決して止めないことを意味するからです。
still_dreaming_1

@ still_dreaming_1 wrt "無責任なプログラマー":この振る舞いが大きな問題であることに同意します。ただし、この種のテスト構造は、実際にはこのような動作を良くも悪くも誘います。悪い開発慣行はさておき、このフォームに対する私の主な反対は、テストのパフォーマンスを本当に損なうことです。実行速度の遅いテストスイートほど悪いことはありません。テストスイートの実行速度が遅いということは、ユーザーがローカルでテストを実行せず、中間ビルドでテストをスキップしたくなることを意味します-繰り返しますが、問題が発生します-これはすべて、高速実行テストを確実に開始することで回避できますと。
つの過去の深夜

2

同じテストで複数のアサーションを持つことは、テストが失敗した場合にのみ問題になります。次に、テストをデバッグするか、例外を分析して、失敗したアサーションを見つける必要がある場合があります。各テストで1つのアサーションを使用すると、通常、何が間違っているかを特定するのが簡単になります。

同じアサーション内の複数の条件として常に書き換えることができるため、複数のアサーションが本当に必要なシナリオを考えることはできません。ただし、たとえば、ステップ間の中間データを検証するためのいくつかのステップがある場合は、入力が不適切なために後のステップがクラッシュするリスクを負うよりも望ましい場合があります。


1
複数の条件を1つのアサートに結合している場合、失敗した場合、失敗したということだけがわかります。複数のアサーションを使用すると、それらの一部(障害までの障害を含む)について具体的に把握できます。返された配列に単一の値が含まれていることを確認することを検討してください。nullではないことを確認し、要素が1つだけで、その要素の値があることを確認します。(プラットフォームによって異なります)値をすぐにチェックするだけで、null参照解除(nullアサーションが失敗するよりは役に立たない)を与える可能性があり、配列の長さはチェックしません。
リチャード

@Richard:結果を取得し、その結果から何かを抽出することは、いくつかのステップのプロセスになるので、答えの2番目の段落でそれを説明しました。
グッファ

2
経験則:テストに複数のアサーションがある場合、それぞれに異なるメッセージが必要です。その後、この問題はありません。
アンソニー

また、NCrunchなどの品質テストランナーを使用すると、テストコードとテスト対象コードの両方で、テストが失敗した行を正確に表示できます。
-4つの過去の深夜

2

テストが失敗すると、次のアサーションが壊れるかどうかもわかりません。多くの場合、問題の原因を特定するための貴重な情報が不足していることを意味します。私の解決策は、1つのアサートを使用することですが、いくつかの値があります。

String actual = "val1="+val1+"\nval2="+val2;
assertEquals(
    "val1=5\n" +
    "val2=hello"
    , actual
);

これにより、失敗したすべてのアサーションを一度に確認できます。ほとんどのIDEが比較ダイアログに文字列の違いを並べて表示するため、複数の行を使用します。


2

単一のテスト関数に複数のアサートがある場合、それらが実行しているテストに直接関連していると思われます。例えば、

@Test
test_Is_Date_segments_correct {

   // It is okay if you have multiple asserts checking dd, mm, yyyy, hh, mm, ss, etc. 
   // But you would not have any assert statement checking if it is string or number,
   // that is a different test and may be with multiple or single assert statement.
}

多くのテストを行うことは、おそらくそれがやり過ぎだと感じたとしても、悪いことではありません。重要で最も重要なテストを行うことがより重要であると主張できます。ですから、アサートするときは、複数のアサートについて心配するのではなく、アサート文が正しく配置されていることを確認してください。複数必要な場合は、複数使用してください。


1

単体テストの目標は、何が失敗しているかについてできるだけ多くの情報を提供するだけでなく、最も基本的な問題を最初に正確に特定するのに役立つことです。別のアサーションが失敗した場合、つまりテスト間に依存関係があるために1つのアサーションが失敗することを論理的に知っている場合、これらを1つのテスト内で複数のアサーションとしてロールすることは理にかなっています。これには、単一のテスト内で最初のアサーションを救済した場合に排除できた明らかな障害でテスト結果を散らかさないという利点があります。この関係が存在しない場合、これらのアサーションを個々のテストに分離することが自然に優先されます。そうでない場合、これらの失敗を見つけるには、すべての問題を解決するためにテスト実行を複数回繰り返す必要があります。

その後、非常に複雑なテストを記述する必要があるような方法でユニット/クラスを設計する場合、テスト中の負担が軽減され、おそらくより良い設計が促進されます。


1

はい、失敗したテストで障害を診断できる十分な情報が得られる限り、複数のアサーションを使用してもかまいません。これは、テストする対象と障害モードに依存します。

適切な単体テストは1つの理由で失敗するはずです。そのため、単体テストごとに1つのアサートを使用する必要があります。

私はそのような定式化が役立つことを発見したことはありません(クラスが変更する理由を1つ持つべきだということは、そのような役に立たない格言の例です)。2つの文字列が等しいという主張を考えてみましょう。これは、2つの文字列の長さが同じで、対応するインデックスの各文字が等しいという主張と意味的に同等です。

一般化して、複数のアサーションのシステムを単一のアサーションとして書き換え、単一のアサーションを一連の小さなアサーションに分解できると言うことができます。

そのため、コードの明快さとテスト結果の明快さに焦点を合わせ、使用するアサーションの数ではなく、その逆を使用してください。


0

答えは非常に簡単です-同じオブジェクト、または2つの異なるオブジェクトの複数の属性を変更する関数をテストし、関数の正確性がそれらすべての変更の結果に依存する場合、アサートする必要がありますそれらの変更のすべてが適切に実行されていること!

論理的な概念のアイデアは得られますが、逆の結論は、機能が複数のオブジェクトを変更してはならないということです。しかし、私の経験では、すべての場合に実装することは不可能です。

銀行取引の論理的な概念を採用します。ほとんどの場合、ある銀行口座から金額を引き出すには、その金額を別の口座に追加する必要があります。これら2つのものを決して分離したくはありません。それらは原子単位を形成します。2つの関数(withdraw / addMoney)を作成して、さらに2つの異なるユニットテストを作成することもできます。ただし、これら2つのアクションは1つのトランザクション内で実行する必要があり、トランザクションが機能することも確認する必要があります。その場合、個々の手順が成功したことを確認するだけでは不十分です。テストでは、両方の銀行口座を確認する必要があります。

そもそも単体テストではなく、統合テストや受け入れテストでテストするより複雑な例があります。しかし、これらの境界は流です、私見!判断するのはそれほど簡単ではありません。状況や個人的な好みの問題です。ある口座からお金を引き出して別の口座に追加することは依然として非常に簡単な機能であり、間違いなく単体テストの候補です。


-1

この質問は、スパゲッティとラザニアのコードの問題のバランスを取るという古典的な問題に関連しています。

複数のアサートがあると、テストが何であるかがわからないスパゲッティ問題に簡単に陥ることがありますが、テストごとに単一のアサートがあると、大きなラザニアに複数のテストがあるため、テストが等しく読みにくくなる可能性があり、どのテストが何が不可能かを見つけることができます。

いくつかの例外がありますが、この場合、振り子を中央に保持することが答えです。


-3

私は、一般に「1つの理由だけで失敗する」ことにさえ同意しません。さらに重要なことは、テストが短く、明らかに読みやすいことです。

ただし、これは常に達成できるとは限りません。テストが複雑な場合は、(長い)わかりやすい名前を付けて、テストするものの数を少なくする方が理にかなっています。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.