Pregunta

Estoy tratando de definir una manera de simular un caso en el acceso a una base de datos sin necesidad de acceder ... Eso es probablemente suena bastante loco, pero no lo es.

Este es un ejemplo de un método me gustaría probar:

    public IDevice GetDeviceFromRepository(string name)
    {
        IDevice device = null;
        IDbConnection connection = new SqlConnection(ConnectionString);
        connection.Open();
        try
        {
            IDbCommand command = connection.CreateCommand();
            command.CommandText = string.Format("SELECT DEVICE_ID,DEVICE_NAME FROM DEVICE WHERE DEVICE_NAME='{0}'", name);
            IDataReader dataReader = command.ExecuteReader();
            if(dataReader.NextResult())
            {
                device = new Device(dataReader.GetInt32(0),dataReader.GetString(1));
            }
        }
        finally
        {
            connection.Close();
        }
        return device;
    }

Estoy pretendiendo burlarse IDataReader así que puede controlar lo que está siendo leído. Algo así (con marco Moq):

    [TestMethod()]
    public void GetDeviceFromRepositoryTest()
    {
        Mock<IDataReader> dataReaderMock = new Mock<IDataReader>();
        dataReaderMock.Setup(x => x.NextResult()).Returns(true);
        dataReaderMock.Setup(x => x.GetInt32(0)).Returns(000);
        dataReaderMock.Setup(x => x.GetString(1)).Returns("myName");
        Mock<IDbCommand> commandMock = new Mock<IDbCommand>();
        commandMock.Setup(x => x.ExecuteReader()).Returns(dataReaderMock.Object);
        Mock<RemoveDeviceManager> removeMock = new Mock<RemoveDeviceManager>();
        removeMock.Setup()
        RemoveDeviceManager target =new RemoveDeviceManager(new Device(000, "myName")); 
        string name = string.Empty; 
        IDevice expected = new Device(000, "myName"); // TODO: Initialize to an appropriate value
        IDevice actual;
        actual = target.GetDeviceFromRepository(name);
        Assert.AreEqual(expected.SerialNumber, actual.SerialNumber);
        Assert.AreEqual(expected.Name, actual.Name);
    }

Mi pregunta es si puedo forzar método GetDeviceFromRepository para reemplazar IDataReader por uno burlado.

¿Fue útil?

Solución

A pesar de que está actualmente utiliza Moq Creo que la funcionalidad que está buscando no se puede achived sin la inyección de dependencia a menos que utilice Typemock aislador ( Renuncia - trabajé en Typemock ).

aislador tiene una característica llamada "objetos" futuros que permitan la sustitución de una instanciación futuro de un objeto con un objeto falsa creada anteriormente:

 // Create fake (stub/mock whateever) objects
 var fakeSqlConnection = Isolate.Fake.Instance<SqlConnection>();
 var fakeCommand = Isolate.Fake.Instance<SqlCommand>();
 Isolate.WhenCalled(() => fakeSqlConnection.CreateCommand()).WillReturn(fakeCommand);

 var fakeReader = Isolate.Fake.Instance<SqlDataReader>();
 Isolate.WhenCalled(() => fakeCommand.ExecuteReader()).WillReturn(fakeReader);

 // Next time SQLConnection is instantiated replace with our fake
 Isolate.Swap.NextInstance<SqlConnection>().With(fakeSqlConnection);

Otros consejos

Me gustaría que el problema aquí es su dependencia directa en última instancia a SqlConnection . Si desea utilizar alguna variante de la inyección de dependencia tal que su código tiene acceso a la IDbCommand sin saber cómo se construye, que sería capaz de inyectar su maqueta sin mucho de una molestia.

Entiendo que esto no acaba de responder a su pregunta, pero a la larga, haciendo cosas como se describe os dará mucho mejor la capacidad de prueba.

Estoy de acuerdo con la respuesta de Frank que se mueve hacia la inyección de dependencia es la mejor solución a largo plazo, pero hay algunos pasos intermedios que puede tomar para que moverse en esa dirección sin morder todo el asunto.

Una cosa es mover la construcción de la clase IDbConnection en un método virtual protegida dentro de su clase:

protected virtual IDbConnection CreateConnection()
{
    return new SqlConnection(ConnectionString);
}

A continuación, puede crear una versión de prueba de su clase, así:

public class TestingRemoteDeviceManager : RemoteDeviceManager
{
   public override IDbConnection CreateConnection()
   {
         IDbConnection conn = new Mock<IDbConnection>();
         //mock out the rest of the interface, as well as the IDbCommand and
         //IDataReader interfaces
         return conn;
   }
}

Esto devuelve un IDbConnection Mock o falso en lugar de la SqlConnection concreto. Esa falsa puede entonces devolver un objeto IDbCommand falsa, que puede devolver un objeto IDataReader falso.

El mantra es test hasta que el miedo se transforma en el aburrimiento . Creo que ha cruzado esa línea aquí. Si desea tomar el control del lector de datos, entonces el único código que está probando es la siguiente:

device = new Device(dataReader.GetInt32(0),dataReader.GetString(1));

No hay casi nada que probar aquí, lo cual es bueno: la capa de datos debe ser simple y estúpida. Así que no trate de poner a prueba su unidad de capa de datos. Si usted siente que debe probarlo, entonces la integración-prueba contra una base de datos real.

Por supuesto, ocultando su capa de datos detrás de una interfaz de IDeviceRepository de modo que usted puede burlarse fácilmente con el fin de probar otro código es una buena idea.

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