Pregunta

Así que, básicamente, tengo una clase abstracta que tiene un identificador único incrementales - Primitive. Cuando se instancia un Primitive (o más precisamente, una heredera de Primitive), el ID se incrementa - hasta el punto donde el ID desborda -. Momento en el cual, añado un mensaje a la excepción y volver a lanzar

OK, que todo funciona bien ... pero estoy tratando de probar esta funcionalidad y nunca he utilizado burlona antes. Sólo tengo que hacer suficientes Primitivas para el ID de desbordamiento y afirman que se lanza en el momento adecuado.

  • No es razonable para crear instancias de 2 mil millones de objetos para hacer esto! Sin embargo, no veo otra manera.
  • No sé si estoy usando burlarse correctamente? (Estoy usando Moq.)

Aquí está mi prueba (xUnit):

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

y

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

supongo que estoy haciendo mal - así que ¿cómo puedo probar esto correctamente

?
¿Fue útil?

Solución

No es necesario burlarse de esto. Se utiliza burlarse cuando dos clases trabajan juntos y que desea reemplazar una clase con una maqueta (falso) uno por lo que sólo tiene que probar que el otro . Este no es el caso en su ejemplo.

No obstante es una forma que podría utilizar burla, y que solucione el problema con los casos 2bln. Si se separa la generación de identificación de la clase Primitive y el uso de un generador, que puede burlarse del generador . Un ejemplo:

He cambiado Primitive utilizar un generador proporcionado. En este caso se configura en una variable estática, y hay mejores maneras, pero como ejemplo:

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

A continuación, se convierte en el caso de test:

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

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

Esto ejecutará mucho más rápido y ahora sólo se está probando si las obras generador ID.

Ahora, cuando por ejemplo, querer prueba de que la creación de una nueva realidad primitiva pide la ID, puede probar lo siguiente:

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

Ahora se han separado las diferentes preocupaciones y puede probar por separado.

Otros consejos

El punto de la maqueta es simular un recurso externo. No es lo que quiere, desea probar su objeto, sin maqueta necesaria en este Szenario. Sólo una instancia de los 2 mil millones de objetos si te gusta, no se pierde nada ya que la GC se tire los viejos casos (pero puede tomar un tiempo para completa).

Id' en realidad añadir otro constructor que acepta un valor strarting para el contador de identidad, por lo que en realidad se puede comenzar cerca de int.MaxValue y por lo tanto no es necesario que instatiate tantos objetos.

Además, sólo desde la fuente readin me puede decir que su objeto no pasará la prueba. ; -)

Usted tiene dos problemas al horno en esta pregunta:

  1. ¿Cómo unidad de prueba de una clase abstracta, que no se puede instanciar.
  2. ¿Cómo funcionalidad de prueba de unidad eficiente que requiere de dos mil millones de casos para ser creados y destruidos.

Creo que las soluciones son bastante simples, a pesar de que tendrá que volver a pensar en la estructura de su objeto ligeramente.

Para el primer problema, la solución es tan simple como añadir una falsificación que hereda Primitive, pero añade ninguna funcionalidad, a su proyecto de prueba. A continuación, puede crear una instancia de la clase falsa en su lugar, y todavía estará probar la funcionalidad del Primitive.

public class Fake : Primitive { }

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

Para el segundo problema, me gustaría añadir un constructor que toma un int para el ID anterior, y el encadenamiento constructor para su uso "no lo necesitan" en su código real. (? Pero, ¿cómo se llega a conocer del ello previa de lo contrario ¿No puedes establecer que a int.MaxValue-1 en la configuración de la prueba?) Piense en ello como la inyección dependecy, pero usted no está inyectando algo compleja; estás inyectando una int sencilla. Podría ser algo como lo siguiente:

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

Todo se ha dicho en las otras respuestas. Sólo quiero que le muestre una alternativa, tal vez esto es de alguna manera interesante para usted.

Si ha realizado el campo _previousId de su clase Primitive internal (e incluyó el atributo InternalsVisibleTo respectiva, por supuesto), entonces su prueba podría ser tan simple como esto con el herramienta Typemock aislador :

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

Por supuesto, Typemock viene con algunos costes de licencia, pero sin duda hace la vida mucho más fácil y le ahorra un montón de tiempo, si usted tiene que escribir grandes cantidades de código de prueba - especialmente en sistemas que no pueden verificarse fácilmente o son incluso imposible a prueba con un marco de burla libre.

Thomas

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top