使用 MsTest 在 C# 中模拟 SqlConnection、SqlCommand 和 SqlReader
Mocking SqlConnection, SqlCommand and SqlReader in C# using MsTest
我遇到了这个 and I'm interested in implementing the second answer using Fake. Here's 。
我并没有真正理解那里的所有概念,我仍在阅读和理解 documentation,有人可以帮助使用我的代码吗,我正在尝试访问客户列表以了解如何使用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: Unable to cast object of type
'Castle.Proxies.IDbConnectionProxy' to type
'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"));
将在生产中使用相同的方法连接到服务器。
我遇到了这个
我并没有真正理解那里的所有概念,我仍在阅读和理解 documentation,有人可以帮助使用我的代码吗,我正在尝试访问客户列表以了解如何使用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: Unable to cast object of type 'Castle.Proxies.IDbConnectionProxy' to type '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"));
将在生产中使用相同的方法连接到服务器。