我遇到了这个答案,我有兴趣使用 Fake 实现第二个答案。这是另一个。
我并没有真正理解那里的所有概念,我仍在阅读和理解文档,有人可以帮助使用我的代码,我正在尝试访问有关如何在此处使用 Fake/Shim/Stub/Mock 的客户列表?
您也可以重写FindAll
方法,以防万一要重构它以接受依赖项。
讨论后编辑
public class Data
{
private Func<IDbConnection> Factory { get; }
public Data(Func<IDbConnection> factory)
{
Factory = factory;
}
public IList<Customer> FindAll()
{
using (var connection = Factory.Invoke())
{
const string sql = "SELECT Id, Name FROM Customer";
using (var command = new SqlCommand(sql, (SqlConnection) connection))
{
command.Connection.Open();
using (var reader = command.ExecuteReader())
{
IList<Customer> rows = new List<Customer>();
while (reader.Read())
{
rows.Add(new Customer
{
Id = reader.GetInt32(reader.GetOrdinal("Id")),
Name = reader.GetString(reader.GetOrdinal("Name"))
});
}
return rows;
}
}
}
}
}
客户
public class Customer
{
public int Id { get; set; }
public string Name { get; set; }
}
单元测试
[TestMethod]
public void TestDB()
{
var commandMock = new Mock<IDbCommand>();
var readerMock = new Mock<IDataReader>();
commandMock.Setup(m => m.ExecuteReader()).Returns(readerMock.Object).Verifiable();
var parameterMock = new Mock<IDbDataParameter>();
commandMock.Setup(m => m.CreateParameter()).Returns(parameterMock.Object);
commandMock.Setup(m => m.Parameters.Add(It.IsAny<IDbDataParameter>())).Verifiable();
var connectionMock = new Mock<IDbConnection>();
connectionMock.Setup(m => m.CreateCommand()).Returns(commandMock.Object);
var data = new Data(() => connectionMock.Object);
var result = data.FindAll();
Console.WriteLine(result);
}
错误
有一个依赖关系打嗝,System.Data.SqlClient
添加,另一个错误随之而来。
System.InvalidCastException: 无法强制转换类型的对象 "Castle.Proxies.IDbConnectionProxy"键入 'System.Data.SqlClient.SqlConnection'.
指向此行
using (var command = new SqlCommand(sql, (SqlConnection) connection))
应该重构所测试的目标方法,以依赖于抽象而不是实现问题。
例如
public class Data {
private Func<IDbConnection> Factory { get; }
public Data(Func<IDbConnection> factory) {
Factory = factory;
}
public IList<Customer> FindAll() {
using (IDbConnection connection = Factory.Invoke()) {
const string sql = "SELECT Id, Name FROM Customer";
using (IDbCommand command = connection.CreateCommand()) {
command.CommandText = sql;
connection.Open();
using (IDataReader reader = command.ExecuteReader()) {
IList<Customer> rows = new List<Customer>();
while (reader.Read()) {
rows.Add(new Customer {
Id = reader.GetInt32(reader.GetOrdinal("Id")),
Name = reader.GetString(reader.GetOrdinal("Name"))
});
}
return rows;
}
}
}
}
}
从那里可以模拟抽象,使其在单元测试隔离时按预期运行。
[TestClass]
public class DataTests{
[TestMethod]
public void Should_Return_Customer() {
//Arrange
var readerMock = new Mock<IDataReader>();
readerMock.SetupSequence(_ => _.Read())
.Returns(true)
.Returns(false);
readerMock.Setup(reader => reader.GetOrdinal("Id")).Returns(0);
readerMock.Setup(reader => reader.GetOrdinal("Name")).Returns(1);
readerMock.Setup(reader => reader.GetInt32(It.IsAny<int>())).Returns(1);
readerMock.Setup(reader => reader.GetString(It.IsAny<int>())).Returns("Hello World");
var commandMock = new Mock<IDbCommand>();
commandMock.Setup(m => m.ExecuteReader()).Returns(readerMock.Object).Verifiable();
var connectionMock = new Mock<IDbConnection>();
connectionMock.Setup(m => m.CreateCommand()).Returns(commandMock.Object);
var data = new Data(() => connectionMock.Object);
//Act
var result = data.FindAll();
//Assert - FluentAssertions
result.Should().HaveCount(1);
commandMock.Verify(); //since it was marked verifiable.
}
}
对于集成测试,可以使用与数据库的实际连接来验证功能
var data = new Data(() => new SqlConnection("live connection string here"));
在生产中将使用相同的方法来连接到服务器。
这样做的问题是IDbCommand没有方法ExecuteNonQueryAsync或任何其他Async方法。
为什么我应该删除异步功能只是为了测试