Mocking SqlConnection, SqlCommand and SqlReader in C# using MsTest

后端 未结 1 888
太阳男子
太阳男子 2021-01-20 12:01

I came across this answer and I\'m interested in implementing the second answer using Fake. Here\'s another one.

I\'m not really understanding all the concepts ther

相关标签:
1条回答
  • 2021-01-20 12:48

    Target method under test should be refactored to depend on abstractions and not implementation concerns.

    For example

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

    From there the abstractions can be mocked to behave as expected when unit testing isolation.

    [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.
        }
    }
    

    For integration tests an actual connection to a database can be used to verify functionality

    var data = new Data(() => new SqlConnection("live connection string here"));
    

    The same approach would be used in production to connect to the server.

    0 讨论(0)
提交回复
热议问题