6

NUnit のパラメーター化されたテスト方法を理論に変更したいとしましょう。理論に関する限り、主張が通用するすべての仮定/前提条件を定義する必要があります。NUnit のドキュメントによると:

[理論をパラメーター化されたテストと比較する場合] 一方、理論は、特定の仮定を満たすすべての引数について、その主張のすべてが合格するという一般的な声明を出します。

しかし、私が理解しているように、これは呼び出された PUT のコードは基本的に仮定に変換する必要があることを意味します。完全に。

では、理論を持っている意味は何ですか?私たちのアルゴリズムは2回書かれるからです。1 つ目はテスト可能なコードとして、2 つ目は理論上の仮定として。したがって、アルゴリズムにバグを導入した場合、コードとテストの両方に同じバグが含まれる可能性があります。それでは、ポイントは何ですか?

理解を深めるための例

数字のみをサポートするチェックサム メソッドがあり、理論を使用してテストしたいとします。理論を書きましょう:

static Regex rx = new Regex(@"^\d+$", RegexOptions.Compiled);

[Theory]
public void ChecksumTheory(string value)
{
    Assume.That(!string.IsNotNullOrWhiteSpace(value));
    Assume.That(value.Length > 1); // one single number + checksum = same number twice
    Assume.That(rx.IsMatch(value));

    var cc = new ChecksumValidator();

    bool result = cc.ValidateValue(value);

    Assert.IsTrue(result); // not really as algorithm assumptions are missing
}

これは非常に優れた理論ですが、テストされたコード アルゴリズムを実際に実装し、それを一連の仮定として表現しなければ、明示的なアルゴリズムの仮定がなければ、検証の結果がどうなるかを知ることができないため、そのアサーションは合格しません。

追加情報

入力状態に関する仮定、つまり特定の値が正しく設定されていること、またはそれらの組み合わせが適切であることを確認することのみを提供する必要がある場合、理論はかなり自明で簡潔に見えます。

[Theory]
public void Person_ValidateState(Person input)
{
    Assume.That(input.Age < 110);
    Assume.That(input.Birth < input.Death || input.Death == null);
    ...
}

質問

  1. すべてのアサートが合格するのに十分な仮定を提供する必要があるのに、なぜ単体テスト理論を書くのでしょうか?
  2. すべてのアルゴリズムの仮定を提供することによって車輪を再発明したくない場合、正しい仮定をどのように提供すればよいでしょうか?
  3. そうでない場合、自分の理論を NUnit 理論の良い例にするにはどのように書き直せばよいでしょうか?
  4. とにかく、テスト理論の(作成者による)意図された使用は何ですか?
4

1 に答える 1

3

理論とパラメータ化されたテスト

また、パラメーター化されたテストを使用する代わりに、テストに仮定を導入することも目指しています。でも、同じような考えでまだ始めていません。

仮定の目的は、指定された入力を、フィルタを適用することによって、数え切れない、または膨大な量の完全な値のセットからのサブセットとして記述することです。これにより、上記のコードは完全に正しいですが、この場合、否定的な結果をテストするためにいくつかの同様のテストを作成する必要があります。の結果が でcc.ValidateValue(...)ある場合false。繰り返しになりますが、わかりやすくするために、この単純な関数のパラメーター化されたテストには、厳選されたパラメーターの適切な選択に依拠します。

一方、仮定は、より複雑なビジネス ロジックのテストに役立つ場合があります。おしゃれな車でいっぱいのガレージがあり、遠く離れた地形でガソリンをぶっ壊したいと思っていると想像してください。また、これがビジネス要件であり、そのためのテストを作成する必要があるとします (これはなんて素晴らしいことでしょう!)。次に、次のようなテストを記述できます。

[Theory]
public void CarCanDriveOnMuddyGround(Car car)
{
    Assume.That(car.IsFourWheelDrive);
    Assume.That(car.HasMotor);
    Assume.That(car.MaxSpeed > 50);
    Assume.That(car.Color != "white");

    bool result = car.DriveWithGivenSpeedOn<MuddyGround>(50);

    Assert.IsTrue(result);
}

これが BDD アプローチとどのように強く関連しているかがわかりますか? あなたと同じように、単純な単体テストに仮定を使用することについても、あまり確信が持てません。しかし、さまざまなテスト レベル (ユニット、統合、システム、ユーザーの受け入れ) に応じて、テスト機能 (パラメーター化、アサーション) にさまざまなアプローチを使用することをお勧めします。

仮定におけるアルゴリズムの詳細について

あなたの特定の問題についてもう一度考えてみてください。今、私はあなたのポイントを得ました。私の言葉で言えば、特定の値が正の結果をもたらすと断言する前に、その値が正の結果をもたらすと仮定する必要があります。右?理論が常に機能するとは限らない、かなり良い例を見つけたと思います。

とにかく、少し単純な例で解決しようとしました(読みやすくするため)。しかし、あまり説得力がないことは認めます。

public class TheoryTests
{
    [Datapoints]
    public string[] InvalidValues = new[] { null, string.Empty };

    [Datapoints]
    public string[] PositiveValues = new[] { "good" };

    [Datapoints]
    public string[] NegativeValues = new[] { "Bad" };

    private bool FunctionUnderTest(string value)
    {
        return value.ToLower().Equals(value);
    }

    [Theory]
    public void PositiveTest(string value)
    {
        Assume.That(!string.IsNullOrEmpty(value));

        var result = FunctionUnderTest(value);

        Assert.True(result);
    }

    [Theory]
    public void PassingPositiveTest(string value)
    {
        Assume.That(!string.IsNullOrEmpty(value));
        Assume.That(!NegativeValues.Contains(value));

        var result = FunctionUnderTest(value);

        Assert.True(result);
    }
}

PositiveTestアルゴリズムの仮定が欠落しているため、明らかに失敗します。PassingPositiveTestテストが失敗するのを防ぐ本体の 2 行目を参照してください。もちろん欠点は、これが実際には例に基づくテストであり、純粋な理論に基づくテストではないことです。より良いアイデアを歓迎します。

于 2013-10-16T06:56:42.863 に答える