如何使用Moq模拟SqlDataReader - 更新
我是新来的moq和设置模拟,所以我可以做一点帮助。如何使用Moq模拟SqlDataReader?如何使用Moq模拟SqlDataReader - 更新
更新
经过进一步的测试,这是我到目前为止有:
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;
}
你的是,当我在我的GetTestData()方法做reader.Read它总是空的问题。我需要知道如何做类似的东西
reader.Stub(x => x.Read()).Repeat.Once().Return(true)
Moq有能力在方法执行后运行一些代码。它被称为“回调”。 修改代码,这样一来,它会工作:
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;
}
但是,如果将需要的IDataReader到不仅包含单列,但有几个?那么,这里是一个示例:
// 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;
}
完美谢谢我正在寻找的东西。 – lancscoder 2010-05-05 09:35:18
为了防止这种情况对其他人有帮助,在Returns方法中()=>中的方括号是至关重要的......如果你不使用它们,你会发现自己陷入了无限循环 – Liath 2013-04-10 11:07:58
在一些测试问题尝试设置DataReader.Read()为true一个循环,然后将其设置为false。犀牛模拟有Repeat.Once()选项,但我无法在Moq中找到类似的方法(我可能在这里是错误的)。
测试这个的主要原因是将读取器转换为相关数据类型的扩展方法,所以最后我删除了while循环并只访问了我的模拟器中设置的值。代码如下:
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;
}
不是一个理想的解决方案,但它的工作原理。如果有人知道最好留下评论谢谢。
我只是想弄明白自己。不知道这是Moq中的新功能,但似乎有比@ Monsignor的答案更简单的方法。
使用Moq的SetupSequence
方法。您的代码可以简化为:
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;
}
这不会让你嘲笑一个SqlDataReader
但如果你的函数返回一个DbDataReader
(基类的SqlDataReader
)或IDataReader
嘲笑它只是使用DataTable
或easist方式一个DataSet
并且调用它的CreateDataReader()
函数并返回它。
首先,在一个单独的项目中,像正常一样运行查询以生成一些测试数据,并使用WriteXmlSchema
生成.xsd文件和WriteXml
函数来保存测试数据。
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");
}
}
在您的测试项目中添加TestDataSet.xsd
到项目,并确保它的MSDataSetGenerator
自定义工具(应该是默认有它)。这将导致DataTable
派生类名为TestDataSet
被生成具有您的查询架构。
然后将TestDataSetData.xml
作为资源添加到您的测试项目中。最后,在您的测试中,使用您生成的xml文件中的文本创建TestDataSet
并调用ReadXml
。
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);
这将创建一个数据读取器,将行为就像将已经从SQL查询返回,它甚至支持之类的东西返回多个结果集的数据读取器。
我没有模拟SqlDataReader的经验,但是,如果可以的话,你应该模拟接口。我已经查找了你,也许这篇文章可以帮助雅:] http://stackoverflow.com/questions/1792984/mocking-a-datareader-and-getting-a-rhino-mocks-exceptions-expectationviolationexc它使用Rhinomocks但这个想法是一样的。建议那里,你应该模拟IDataReader。当你嘲笑它的时候,你不应该在模拟^^(^)上做任何问题^^如果你已经试着嘲笑一个接口,也许你可以通过发布一些示例代码告诉我们你卡在哪里:] – Bas 2010-04-15 09:13:22