Domanda

Quindi, in pratica, ho una classe astratta che ha un unico, ID incrementale - Primitive. Quando un Primitive (o, più precisamente, un erede dei Primitive) viene istanziato, l'ID viene incrementato - fino al punto in cui l'ID trabocca -. A questo punto, aggiungo un messaggio per l'eccezione e rethrow

OK, che tutto funziona benissimo ... ma sto cercando di testare questa funzionalità e non ho mai usato beffardo prima. Ho solo bisogno di fare abbastanza Primitives per l'ID di troppo pieno e affermare che getta al momento giusto.

  • Non è ragionevole per istanziare 2 miliardi di oggetti per fare questo! Tuttavia non vedo altro modo.
  • Non so se sto utilizzando beffarda correttamente? (Sto usando Moq.)

Ecco la mia prova (xUnit):

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

e

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);
        }
    }
}

presumo che sto facendo male - così come faccio a testare questo correttamente

?
È stato utile?

Soluzione

Non è necessario beffardo per questo. Si utilizza beffarda quando due classi lavorano insieme e si vuole sostituire una classe con una finta (finta) un modo da avere solo per testare l'altra . Questo non è il caso nel tuo esempio.

V'è tuttavia un modo si potrebbe usare deride, e che le correzioni il problema con le istanze 2 miliardi. Se si separano la generazione ID dalla classe Primitive e utilizzare un generatore, si può prendere in giro il generatore . Un esempio:

Ho cambiato Primitive di utilizzare un generatore fornito. In questo caso è impostata su una variabile statica, e ci sono modi migliori, ma come esempio:

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);
        }
    }
}

Poi, il banco di prova diventa:

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

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

Questo verrà eseguito molto più velocemente e ora si sta testando solo se i lavori del generatore ID.

Ora, quando per esempio Vuoi mettere alla prova che la creazione di una nuova primitiva chiede in realtà per l'ID, si potrebbe provare la seguente:

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();
}

Ora avete separato i diversi problemi e li possibile testare separatamente.

Altri suggerimenti

Il punto del finto è quello di simulare una risorsa esterna. Non è ciò che si vuole, si vuole testare tuo oggetto, non finto necessario in questo Szenario. Basta istanziare i 2 miliardi di oggetti, se vi piace, non fa male dal momento che il GC buttare via i vecchi casi (ma potrebbe richiedere un po 'per completa).

Id' in realtà aggiungere un altro costruttore che accetta un valore senza vincoli minimi per il contatore di identità, in modo che si può effettivamente iniziare vicino alla int.MaxValue e quindi non c'è bisogno di instatiate come molti oggetti.

Inoltre, proprio dalla readin la fonte posso dire che l'oggetto avrà esito negativo il test. ; -)

Si hanno due problemi cotto in questa domanda:

  1. Come unit test una classe astratta, che non si può instantiate.
  2. Come funzionalità unit test in modo efficiente che richiede due miliardi di istanze per essere creati e distrutti.

Credo che le soluzioni sono abbastanza semplici, anche se si dovrà ripensare la struttura del vostro oggetto un po '.

Per il primo problema, la soluzione è semplice come l'aggiunta di un falso che eredita Primitive, ma aggiunge alcuna funzionalità, al progetto di test. È quindi possibile creare un'istanza la classe falso, invece, e sarete comunque testare la funzionalità di Primitive.

public class Fake : Primitive { }

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

Per il secondo problema, mi piacerebbe aggiungere un costruttore che prende un int per l'ID precedente, e l'uso del costruttore concatenamento a "non averne bisogno" nel codice vero e proprio. (? Ma come si arriva a conoscere della id precedente altrimenti Non puoi impostare che a int.MaxValue-1 nel setup del test?) Pensate a come iniezione dependecy, ma non stai iniettando nulla complessa; sei solo iniettando una semplice int. Potrebbe essere qualcosa in queste righe:

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);
    }
}

Tutto è stato detto in altre risposte. Voglio solo mostrarvi un'alternativa, forse questo è in qualche modo interessante per voi.

Se hai fatto il campo _previousId della classe Primitive internal (e incluso il relativo attributo InternalsVisibleTo, ovviamente), quindi il test potrebbe essere semplice come questo con il Typemock Isolator strumento:

[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));
}

Certo, Typemock viene fornito con alcuni costi di licenza, ma rende sicuramente la vita molto più facile e consente di risparmiare un sacco di tempo, se si deve scrivere grandi quantità di codice di prova - in particolare su sistemi che non sono facilmente testati o che sono addirittura impossibile per test con un quadro di scherno gratuito.

Thomas

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top