2010-04-15 14 views
30

Jestem nowy, aby moq i ustawienie makiety, więc mogłem zrobić przy niewielkiej pomocy. W jaki sposób makiety SqlDataReader za pomocą Moq?Jak wyśmiać SqlDataReader za pomocą Moq - Aktualizacja

Aktualizacja

Po dalszych badań jest to, co mam tak daleko:

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

występujący problem jest wtedy, gdy robię reader.Read w sposobie mojego GetTestData() jej zawsze pusty. Muszę wiedzieć, jak coś zrobić jak

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

jak na przykład: makiety nosorożca Mocking a DataReader and getting a Rhino.Mocks.Exceptions.ExpectationViolationException: IDisposable.Dispose(); Expected #0, Actual #1

+0

Nie mam doświadczenia w kpieniu z SqlDataReader, ale jeśli możesz, powinieneś kpić z interfejsu. Sprawdziłem to dla ciebie i może ten artykuł mógłby ci pomóc:] http://stackoverflow.com/questions/1792984/mocking-a-datareader-and-getting-a-rhino-mocks-exceptions-expectationviolationexc Używa Rhinomocks, ale pomysł jest taki sam. Sugerowane tam, powinieneś wykpić IDataReader. Kiedy wyśmiałeś się z tego, nie powinieneś mieć problemów z wykonywaniem .Setups() na próbach ^^ Jeśli już próbowałeś kpić z interfejsu, może mógłbyś pokazać nam, gdzie utknąłeś, umieszczając przykładowy kod:] – Bas

Odpowiedz

54

Moq ma możliwość uruchomienia kodu po wykonaniu metody. Nazywa się "Callback". zmodyfikować kod w ten sposób i to będzie działać:

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

Ale co, jeśli to będzie konieczne IDataReader zawierać nie tylko jeden wiersz, ale kilka? Oto próbka:

// 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

Idealne dzięki właśnie tego, czego szukałem. – lancscoder

+1

Na wszelki wypadek, gdy to pomoże komuś innemu, w metodzie Zwroty nawiasy w() => są krytyczne ... jeśli ich nie użyjesz, znajdziesz się w nieskończonej pętli – Liath

1

Po niektóre badania problemu próbuje ustawić DataReader.Read(), aby prawdziwe dla jednej pętli, a następnie ustawiając go na false. Rhino Mock ma opcję Repeat.Once(), ale nie mogłem znaleźć podobnej metody w Moq (może się mylę tutaj).

Głównym powodem przetestowania tego było rozszerzenie metod przekształcania czytnika na odpowiedni typ danych, więc w końcu usunąłem pętlę while i po prostu uzyskałem dostęp do wartości, które zostały ustawione w moim symulatorze. Kod wygląda następująco:

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

Nie jest to idealne rozwiązanie, ale działa. Jeśli ktoś wie lepiej, zostaw komentarz dzięki.

8

Po prostu próbowałem to rozgryźć. Nie wiem, czy jest to nowa funkcja w Moq, ale wydaje się, że istnieje prostsza metoda niż odpowiedź @ Monsignor.

Użyj metody Moq: SetupSequence. Kod staje się po prostu:

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

nie pozwala szydzić z SqlDataReader ale jeśli funkcja jest zwrócenie (klasy podstawy SqlDataReader) DbDataReader lub IDataReader się easist drogę do Mock to jest po prostu użyć DataTable lub a DataSet i wywołaj jego funkcję CreateDataReader() i zwróć ją.

Po pierwsze, w oddzielnym projekcie, uruchom zapytanie tak, jak zwykle, aby wygenerować niektóre dane testowe i użyj WriteXmlSchema do wygenerowania pliku .xsd i funkcji WriteXml do przechowywania danych testowych.

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

W projekcie testowym dodać TestDataSet.xsd do projektu i upewnić się, że posiada narzędzie niestandardową MSDataSetGenerator (powinien mieć domyślnie). Spowoduje to wygenerowanie klasy pochodnej DataTable o nazwie TestDataSet, która ma schemat zapytania.

Następnie dodaj TestDataSetData.xml jako zasób do swojego projektu testowego.Na koniec w teście utworzymy TestDataSet i zadzwonimy pod numer ReadXml, używając tekstu z wygenerowanego pliku xml.

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

To stworzy czytnika danych, który będzie działał podobnie jak czytnik danych, które zostały zwrócone z kwerendy SQL i obsługuje nawet takie rzeczy jak wielu zestawów wynikowych zwróconych.

Powiązane problemy