1

つまり、基本的に、一意のインクリメンタルIDを持つ抽象クラスがあります- PrimitivePrimitive(またはより正確にはの継承者)Primitiveがインスタンス化されると、IDがインクリメントされます(IDがオーバーフローするポイントまで)。その時点で、例外にメッセージを追加して再スローします。

OK、それはすべて正常に機能します...しかし、私はこの機能をテストしようとしており、これまでモックを使用したことはありません。IDがオーバーフローし、適切なタイミングでスローされることを表明するのに十分なプリミティブを作成する必要があります。

  • これを行うために20億個のオブジェクトをインスタンス化するのは無理です!しかし、私は別の方法を見ていません。
  • モッキングを正しく使用しているかどうかわかりませんか?(私は使用してMoqいます。)

これが私のテストです(xUnit):

[Fact(DisplayName = "Test Primitive count limit")]
public void TestPrimitiveCountLimit()
{
    Assert.Throws(typeof(OverflowException), delegate()
    {
        for (; ; )
        {
            var mock = new Mock<Primitive>();
        }
    });
}

と:

public abstract class Primitive
{
    internal int Id { get; private set; }
    private static int? _previousId;

    protected Primitive()
    {
        try
        {
            _previousId = Id = checked (++_previousId) ?? 0;
        }
        catch (OverflowException ex)
        {
            throw new OverflowException("Cannot instantiate more than (int.MaxValue) unique primitives.", ex);
        }
    }
}

私はそれを間違っていると思います-それで、これを正しくテストするにはどうすればよいですか?

4

4 に答える 4

5

これをあざける必要はありません。2つのクラスが連携して動作し、一方のクラスをモック(偽の)クラスに置き換えたい場合はモックを使用するため、もう一方のクラスをテストするだけで済みます。これはあなたの例には当てはまりません。

ただし、モックを使用する方法があり、それによって2blnインスタンスの問題が修正されます。ID生成をクラスから分離しPrimitiveてジェネレーターを使用すると、ジェネレーターをモックできます。例:

Primitive付属のジェネレーターを使用するように変更しました。この場合、それは静的変数に設定されており、より良い方法がありますが、例として:

public abstract class Primitive
{
    internal static IPrimitiveIDGenerator Generator;

    protected Primitive()
    {
        Id = Generator.GetNext();
    }

    internal int Id { get; private set; }
}

public interface IPrimitiveIDGenerator
{
    int GetNext();
}

public class PrimitiveIDGenerator : IPrimitiveIDGenerator
{
    private int? _previousId;

    public int GetNext()
    {
        try
        {
            _previousId = checked(++_previousId) ?? 0;

            return _previousId.Value;
        }
        catch (OverflowException ex)
        {
            throw new OverflowException("Cannot instantiate more than (int.MaxValue) unique primitives.", ex);
        }
    }
}

次に、テストケースは次のようになります。

[Fact(DisplayName = "Test Primitive count limit")]
public void TestPrimitiveCountLimit()
{
    Assert.Throws(typeof(OverflowException), delegate()
    {
        var generator = new PrimitiveIDGenerator();

        for (; ; )
        {
            generator.GetNext();
        }
    });
}

これははるかに高速に実行され、IDジェネレーターが機能するかどうかをテストするだけです。

たとえば、新しいプリミティブを作成すると実際にIDが要求されることをテストする場合は、次のことを試すことができます。

public void Does_primitive_ask_for_an_ID()
{
    var generator = new Mock<IPrimitiveIDGenerator>();

    // Set the expectations on the mock so that it checks that
    // GetNext is called. How depends on what mock framework you're using.

    Primitive.Generator = generator;

    new ChildOfPrimitive();
}

これで、さまざまな懸念事項を分離し、それらを個別にテストできます。

于 2010-11-07T13:29:41.903 に答える
1

モックのポイントは、外部リソースをシミュレートすることです。それはあなたが望むものではありません、あなたはあなたのオブジェクトをテストしたいです、このシナリオではモックは必要ありません。必要に応じて20億個のオブジェクトをインスタンス化するだけで、GCが古いインスタンスを破棄するため、問題はありません(ただし、完了するまでに時間がかかる場合があります)。

Id'は、実際にはIDカウンターのストラッティング値を受け入れる別のコンストラクターを追加します。これにより、実際に近くから開始できるため、int.MaxValue多くのオブジェクトをインスタンス化する必要がなくなります。

また、ソースを読み込んだだけで、オブジェクトがテストに失敗することがわかります。;-)

于 2010-11-07T13:28:31.640 に答える
1

この質問には2つの問題があります。

  1. インスタンス化できない抽象クラスの単体テスト方法。
  2. 20億のインスタンスを作成および破棄する必要があるテスト機能を効率的に単体テストする方法。

オブジェクトの構造を少し考え直す必要がありますが、解決策は非常に単純だと思います。

Primitive最初の問題の解決策は、テストプロジェクトに機能を継承するが追加しない偽物を追加するのと同じくらい簡単です。その後、代わりに偽のクラスをインスタンス化できますが、の機能は引き続きテストされますPrimitive

public class Fake : Primitive { }

// and in your test...
Assert.Throws(typeof(OverflowException), delegate() { var f = new Fake(int.MaxValue); });

2番目の問題では、前のIDをとるコンストラクターを追加し、コンストラクintターチェーンを使用して、実際のコードで「それを必要としない」ようにします。(しかし、他の方法で前のIDを知るにはどうすればよいですか?int.MaxValue-1テストのセットアップでそれを設定できませんか?)依存性注入と考えてください。ただし、複雑なものは何も注入していません。単純なを注入しているだけですint。それはこれらの線に沿った何かである可能性があります:

public abstract class Primitive
{
internal int Id { get; private set; }
private static int? _previousId;

protected Primitive() : Primitive([some way you get your previous id now...])
protected Primitive(int previousId)
{
    _previousId = previousId;
    try
    {
        _previousId = Id = checked (++_previousId) ?? 0;
    }
    catch (OverflowException ex)
    {
        throw new OverflowException("Cannot instantiate more than (int.MaxValue) unique primitives.", ex);
    }
}
于 2010-11-07T13:43:13.620 に答える
1

すべては他の答えで言われています。私はあなたに代替案を示したいだけです、多分これはあなたにとってどういうわけか面白いです。

クラスの_previousIdフィールドを作成した場合(そしてもちろん、それぞれの属性を含めた場合)、TypemockIsolatorツールを使用してテストをこれと同じくらい簡単にすることができます。PrimitiveinternalInternalsVisibleTo

[Fact(DisplayName = "Test Primitive count limit"), Isolated]
public void TestPrimitiveCountLimit()
{
    Primitive._previousId = int.MaxValue;

    Assert.Throws<OverflowException>(() => 
        Isolate.Fake.Instance<Primitive>(Members.CallOriginal, ConstructorWillBe.Called));
}

確かに、Typemockにはライセンス費用がかかりますが、大量のテストコードを作成する必要がある場合、特にテストが簡単ではない、またはテストが不可能なシステムでは、間違いなく作業がはるかに楽になり、多くの時間を節約できます。無料のモックフレームワーク。

トーマス

于 2010-11-07T15:14:31.967 に答える