使用FakeItEasy在NUnit测试中伪造SqlConnection和嵌入式方法

问题描述

我有下面的类和方法,它们将连接到数据库,但是对于测试,我不需要真正的连接,并且需要伪造它。我们正在为此使用FakeItEasy。:

public abstract class HandlerBase
    {
        public string errorMessage;

        private MyActionsDataModel Action
        {
            get
            {
                if (_action == null)
                {
                    _action = new MyActionsDataModel();
                    using (var connection = new SqlConnection(Constants.Connections.MyDatabase))
                    {
                        connection.Open();
                        using (var transaction = connection.BeginTransaction(IsolationLevel.Serializable))
                        {
                            _action.Id = connection.QuerySingle<int>("UpdateAction",transaction: transaction,commandType: CommandType.StoredProcedure,param: Action);
                            transaction.Commit();
                        }
                    }
                }
                return _action;
            }
        }
        private MyActionsDataModel _action;

        public void RecordFailure(AggregateException ex)
        {
            Console.WriteLine("A failure happened:");
            Console.WriteLine(JsonConvert.SerializeObject(ex));
            errorMessage = "Inner Exception\r\n" + ex.Message;
            Action.ErrorOccurredOnUtc = DateTimeOffset.UtcNow;
            Action.ErrorType = ex.GetType().FullName;
            Action.ErrorMessage = errorMessage;
            SaveAction();
        }

        private void SaveAction()
        {
            using (var connection = new SqlConnection(Constants.Connections.MyDatabase))
            {
                connection.Open();
                using (var transaction = connection.BeginTransaction(IsolationLevel.Serializable))
                {
                    connection.Execute("UpdateAction",param: Action);
                    transaction.Commit();
                }
            }
        }
     }

我将在测试中调用的另一个类:

public class MyHandlerType : HandlerBase
    {
        private readonly MyTracker _myTracker;

        public MyHandlerType(MyTracker myTracker) : base()
        {
            _myTracker = myTracker;
        }
    }

我想要的是伪造 Action 参数以及 SaveAction 方法。

这是我要进行的测试,但不确定如何制作假部件。

public class HandlerTests
    {
        [TestCase]
        public void Test_RecordFailure()
        {
            var innerMessage = "Throw AppException for UnitTest.";
            var parentMessage = "Throw AggregationException for UnitTest.";

            var testHandler = new MyHandlerType(null);
            var innerException = new ApplicationException(innerMessage);
            var parentException = new AggregateException(parentMessage,innerException);
            testHandler.RecordFailure(parentException);
            var includeInnerMessage = testHandler.errorMessage.Contains(innerMessage);
            var includeParentMessage = testHandler.errorMessage.Contains(parentMessage);

            Assert.IsTrue(includeInnerMessage);
            Assert.IsTrue(includeParentMessage);
        }
    }

解决方法

当前类与实现方面的考虑紧密相关,这使得孤立地对其进行测试变得困难。

考虑重构类

public abstract class HandlerBase {
    private readonly Lazy<MyActionsDataModel> model;
    private readonly IDbConnectionFactory connectionFactory;

    protected HandlerBase(IDbConnectionFactory connectionFactory) {
        this.connectionFactory = connectionFactory;
        model = new Lazy<MyActionsDataModel>(() => {
            MyActionsDataModel action = new MyActionsDataModel();
            using (DbConnection connection = this.connectionFactory.Create()) {
                connection.Open();
                using (DbTransaction transaction = connection.BeginTransaction(IsolationLevel.Serializable)) {
                    action.Id = connection.QuerySingle<int>("UpdateAction",transaction: transaction,commandType: CommandType.StoredProcedure,param: action);
                    transaction.Commit();
                }
            }
            return action;
        });           
    }

    public string ErrorMessage;

    public void RecordFailure(AggregateException ex) {
        Console.WriteLine("A failure happened:");
        Console.WriteLine(JsonConvert.SerializeObject(ex));
        ErrorMessage = "Inner Exception\r\n" + ex.Message;
        MyActionsDataModel action = model.Value;
        action.ErrorOccurredOnUtc = DateTimeOffset.UtcNow;
        action.ErrorType = ex.GetType().FullName;
        action.ErrorMessage = ErrorMessage;
        saveAction(action);
    }

    private void saveAction(MyActionsDataModel action) {
        using (DbConnection connection = connectionFactory.Create()) {
            connection.Open();
            using (DbTransaction transaction = connection.BeginTransaction(IsolationLevel.Serializable)) {
                connection.Execute("UpdateAction",param: action);
                transaction.Commit();
            }
        }
    }
}

请注意引入显式依赖项

public interface IDbConnectionFactory {
    DbConnection Create();
}

可以实现的

// Connection Factory method
public DbConnection Create() {
    DbConnection connection = new SqlConnection(Constants.Connections.MyDatabase);
    return connection;
}

在进行测试时,锻炼被测对象时可以嘲笑出厂时的行为。

,

这是我的新代码(基于@Nkosi的建议)和测试部分:

代码:

    public abstract class HandlerBase {
        private readonly Lazy<MyActionsDataModel> model;
        private readonly IDbConnectionFactory connectionFactory;
    
        protected HandlerBase(IDbConnectionFactory connectionFactory) {
            this.connectionFactory = connectionFactory;
            model = new Lazy<MyActionsDataModel>(() => {
                MyActionsDataModel action = new MyActionsDataModel();
                using (DbConnection connection = this.connectionFactory.Create()) {
                    connection.Open();
                    using (DbTransaction transaction = connection.BeginTransaction(IsolationLevel.Serializable)) {
                        action.Id = connection.QuerySingle<int>("UpdateAction",param: action);
                        transaction.Commit();
                    }
                }
                return action;
            });           
        }
    
        public string ErrorMessage;
    
        public void RecordFailure(AggregateException ex) {
            Console.WriteLine("A failure happened:");
            Console.WriteLine(JsonConvert.SerializeObject(ex));
            ErrorMessage = "Inner Exception\r\n" + ex.Message;
            MyActionsDataModel action = model.Value;
            action.ErrorOccurredOnUtc = DateTimeOffset.UtcNow;
            action.ErrorType = ex.GetType().FullName;
            action.ErrorMessage = ErrorMessage;
            saveAction(action);
        }
    
        private void saveAction(MyActionsDataModel action) {
            using (DbConnection connection = new SqlConnection(Constants.Connections.MyDatabase)) {
                connection.Open();
                using (DbTransaction transaction = connection.BeginTransaction(IsolationLevel.Serializable)) {
                    connection.Execute("UpdateAction",param: action);
                    transaction.Commit();
                }
            }
        }
    }
    
    public interface IDbConnectionFactory {
        DbConnection Create();
    }
    
    // Connection Factory method
    public DbConnection Create() {
        DbConnection connection = new SqlConnection(Constants.Connections.MyDatabase);
        return connection;
    }


public class MyHandlerType : HandlerBase
    {
        private readonly IDbConnectionFactory _connectionFactory;

        public MyHandlerType(IDbConnectionFactory connectionFactory) : base(connectionFactory)
        {
            _connectionFactory = connectionFactory;
        }
}

测试:

public class HandlerTests
    {
        protected MyHandlerType _subjectUnderTest;
        protected HandlerBase.IDbConnectionFactory _fakeConnectionFactory;

        [SetUp]
        public void Setup()
        {
            _fakeConnectionFactory = A.Fake<HandlerBase.IDbConnectionFactory>();
            A.CallTo(() => _fakeConnectionFactory.Create()).Returns<DbConnection>(new SqlConnection(Constants.Connections.MyDatabase));

            _subjectUnderTest = new MyHandlerType(_fakeConnectionFactory);
        }

        [TestCase]
        public void Test_RecordFailure()
        {
            var innerMessage = "Throw AppException for UnitTest.";
            var parentMessage = "Throw AggregationException for UnitTest.";
            var innerException = new ApplicationException(innerMessage);
            var parentException = new AggregateException(parentMessage,innerException);
            _subjectUnderTest.RecordFailure(parentException);
            var includeInnerMessage = testHandler.errorMessage.Contains(innerMessage);
            var includeParentMessage = testHandler.errorMessage.Contains(parentMessage);

            Assert.IsTrue(includeInnerMessage);
            Assert.IsTrue(includeParentMessage);
        }
    }

相关问答

依赖报错 idea导入项目后依赖报错,解决方案:https://blog....
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下...
错误1:gradle项目控制台输出为乱码 # 解决方案:https://bl...
错误还原:在查询的过程中,传入的workType为0时,该条件不起...
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct...