2010-04-15 21 views
30

Soy nuevo en moq y en la configuración de simulacros, así que podría hacerlo con un poco de ayuda. ¿Cómo simulo un SqlDataReader usando Moq?Cómo simular un SqlDataReader usando Moq - Update

actualización

Después de las pruebas más esto es lo que tengo hasta ahora:

private IDataReader MockIDataReader() 
{ 
    var moq = new Mock<IDataReader>(); 
    moq.Setup(x => x.Read()).Returns(true); 
    moq.Setup(x => x.Read()).Returns(false); 
    moq.SetupGet<object>(x => x["Char"]).Returns('C'); 

    return moq.Object; 
} 

private class TestData 
{ 
    public char ValidChar { get; set; } 
} 

private TestData GetTestData() 
{ 
    var testData = new TestData(); 

    using (var reader = MockIDataReader()) 
    { 
     while (reader.Read()) 
     { 
      testData = new TestData 
      { 
       ValidChar = reader.GetChar("Char").Value 
      }; 
     } 
    } 

    return testData; 
} 

El problema que es cuando no reader.Read en mi método GetTestData() es siempre vacía. Necesito saber cómo hacer algo como

reader.Stub(x => x.Read()).Repeat.Once().Return(true) 

como por ejemplo el rinoceronte simulacro: Mocking a DataReader and getting a Rhino.Mocks.Exceptions.ExpectationViolationException: IDisposable.Dispose(); Expected #0, Actual #1

+0

No tengo experiencia en burlarse del SqlDataReader pero, si puede, debe burlarse de la interfaz. Lo he buscado y tal vez este artículo podría ayudarte ya:] http://stackoverflow.com/questions/1792984/mocking-a-datareader-and-getting-a-rhino-mocks-exceptions-expectationviolationexc Utiliza Rhinomocks pero la idea es la misma. Sugerido allí, debe simular IDataReader. Cuando te burles de eso, no deberías tener problemas para hacer .Setups() en el simulacro ^^ Si ya has intentado burlarse de una interfaz, tal vez podrías mostrarnos dónde te quedas atascado publicando algún código de ejemplo:] – Bas

Respuesta

54

Moq tiene la capacidad de ejecutar código después de que se ejecuta el método. Se llama "devolución de llamada". modificar el código de esta manera y que funcionará:

private IDataReader MockIDataReader() 
{ 
    var moq = new Mock<IDataReader>(); 

    bool readToggle = true; 

    moq.Setup(x => x.Read()) 
     // Returns value of local variable 'readToggle' (note that 
     // you must use lambda and not just .Returns(readToggle) 
     // because it will not be lazy initialized then) 
     .Returns(() => readToggle) 
     // After 'Read()' is executed - we change 'readToggle' value 
     // so it will return false on next calls of 'Read()' 
     .Callback(() => readToggle = false); 

    moq.Setup(x => x["Char"]) 
     .Returns('C'); 

    return moq.Object; 
} 

private class TestData 
{ 
    public char ValidChar { get; set; } 
} 

private TestData GetTestData() 
{ 
    var testData = new TestData(); 

    using (var reader = MockIDataReader()) 
    { 
     testData = new TestData 
     { 
      ValidChar = (Char)reader["Char"] 
     }; 
    } 

    return testData; 
} 

Pero ¿y si se requiere IDataReader para contener no sólo una hilera, pero varios? Bueno, aquí hay una muestra:

// You should pass here a list of test items, their data 
// will be returned by IDataReader 
private IDataReader MockIDataReader(List<TestData> ojectsToEmulate) 
{ 
    var moq = new Mock<IDataReader>(); 

    // This var stores current position in 'ojectsToEmulate' list 
    int count = -1; 

    moq.Setup(x => x.Read()) 
     // Return 'True' while list still has an item 
     .Returns(() => count < ojectsToEmulate.Count - 1) 
     // Go to next position 
     .Callback(() => count++); 

    moq.Setup(x => x["Char"]) 
     // Again, use lazy initialization via lambda expression 
     .Returns(() => ojectsToEmulate[count].ValidChar); 

    return moq.Object; 
} 
+0

Perfecto gracias justo lo que estaba buscando. – lancscoder

+1

En caso de que esto ayude a alguien más, en el método Returns los corchetes en() => son críticos ... si no los usa, se encontrará en un bucle infinito – Liath

1

Después de algunas pruebas el problema está tratando de establecer el DataReader.Read() para cierto para un bucle y luego poniéndolo en falso. Rhino Mock tiene la opción Repeat.Once() pero no pude encontrar un método similar en Moq (podría estar equivocado aquí).

La razón principal para probar esto fueron los métodos de extensión para convertir el lector al tipo de datos relevante, así que al final eliminé el ciclo while y acabo de acceder a los valores que se habían configurado en mi simulacro. El código se ve de la siguiente manera:

private IDataReader MockIDataReader() 
{ 
    var moq = new Mock<IDataReader>(); 
    moq.SetupGet<object>(x => x["Char"]).Returns('C'); 

    return moq.Object; 
} 

private class TestData 
{ 
    public char ValidChar { get; set; } 
} 

private TestData GetTestData() 
{ 
    var testData = new TestData(); 

    using (var reader = MockIDataReader()) 
    { 
     testData = new TestData 
     { 
      ValidChar = reader.GetChar("Char").Value 
     }; 
    } 

    return testData; 
} 

No es una solución ideal, pero funciona. Si alguien sabe mejor deje un comentario gracias.

8

Estaba tratando de resolver esto por mi cuenta. No estoy seguro de si esta es una nueva funcionalidad en Moq, pero parece que hay una manera más simple que la respuesta de @Monignor.

Utilice el método SetupSequence de Moq. Su código se convierte simplemente en:

private IDataReader MockIDataReader() 
{ 
    var moq = new Mock<IDataReader>(); 
    moq.SetupSequence(x => x.Read()) 
     .Returns(true); 
     .Returns(false); 
    moq.SetupGet<object>(x => x["Char"]).Returns('C'); 

    return moq.Object; 
} 
1

Esto no le permite burlas de un SqlDataReader pero si su función se vuelve un (clase La base de SqlDataReader) DbDataReader o la forma mas fácil IDataReader para burlarse de que sólo se utilice un DataTable o a DataSet y llame a su función CreateDataReader() y devuelva eso.

En primer lugar, en un proyecto independiente, ejecute la consulta de manera normal para producir algunos datos de prueba y utilizar el WriteXmlSchema para generar un archivo .xsd y las funciones WriteXml para contener los datos de prueba.

using (var con = new SqlConnection(connectionString)) 
{ 
    con.Open(); 
    using (var cmd = new SqlCommand("Some query", con)) 
    { 

     DataSet ds = new DataSet("TestDataSet"); 
     DataTable dt = new DataTable("FirstSet"); 
     ds.Tables.Add(dt); 
     using (var reader = cmd.ExecuteReader()) 
     { 
      dt.Load(reader); 
     } 

     ds.WriteXmlSchema(@"C:\Temp\TestDataSet.xsd"); 
     ds.WriteXml(@"C:\Temp\TestDataSetData.xml"); 
    } 
} 

En el proyecto de prueba TestDataSet.xsd añadir al proyecto y asegurarse de que tiene la herramienta personalizada de MSDataSetGenerator (debe tener por defecto). Esto causará que se genere una clase derivada DataTable llamada TestDataSet que tenga el esquema de su consulta.

A continuación, agregue TestDataSetData.xml como recurso a su proyecto de prueba.Finalmente, en su prueba, cree el TestDataSet y llame al ReadXml usando el texto del archivo xml que generó.

var resultSet = new TestData.TestDataSet(); 
using (var reader = new StringReader(Resources.TestDataSetData)) 
{ 
    resultSet.ReadXml(reader); 
} 

var testMock = new Mock<DbCommand>(); 

testMock.Setup(x => x.ExecuteReader()) 
    .Returns(resultSet.CreateDataReader); 

testMock.Setup(x => x.ExecuteReaderAsync()) 
    .ReturnsAsync(resultSet.CreateDataReader); 

Esto creará un lector de datos que actuará al igual que el lector de datos que han sido devueltos por la consulta SQL e incluso soporta cosas como varios conjuntos de resultados devueltos.

Cuestiones relacionadas