【问题标题】:How to unit test DBService which uses the Servicestack Funq IOC如何对使用 Servicestack Funq IOC 的 DBService 进行单元测试
【发布时间】:2017-01-31 12:27:52
【问题描述】:

我是一个需要扩展的项目的新手,所以我决定使用 TDD 来快速识别我不完全理解的系统的任何问题。

有一个名为DBService 的类“封装”了所有数据库访问。例如,有一种方法称为getAllCustomers,它返回Customers 的列表。这看起来像这样(这只是一个更好理解的示例):

public class DBService
{
    public IDbConnectionFactory DBFactory { 
        get { return DI.Container.Resolve<IDbConnectionFactory>(); }
    }

    public List<Customer> GetAllCustomers()
    {
        try
        {
            using (var connection = DBFactory.OpenDbConnection())
            {
                var dbResult = connection.Select<Customer>();
                // code ommitted
            }
        }
        catch (Exception e)
        {
            // code ommitted
        }
    }      
}

另一个问题是,在开始时(在 ServiceStack AppHost.Configure 中),如果它们不存在,则创建所有表,并且对于某些表,如果它们存在,则添加一些列等(这可能是稍后添加的更改)

例如,当我现在必须扩展客户并添加另一个字段时,我想以 TDD 样式执行此操作,但我不知道该怎么做。

  1. 我不能注入任何 DBFactory 因为 getter 是私有的
  2. Afaik 我不能将 :memory: 连接字符串用于 OrmLiteConnectionFactory,因为我使用的是 ServiceStack 3.9.74

那么我在这里有什么选择?

【问题讨论】:

    标签: c# unit-testing mocking ormlite-servicestack servicestack-ioc


    【解决方案1】:

    避免使用 Service Locator 反模式并改用构造函数注入。尽量避免在依赖类中直接使用 DI 容器。它将您的类与不属于该类的关注点紧密耦合在一起,使得单独测试类变得困难。

    public class DBService {
        private readonly IDbConnectionFactory connectionFactory;
    
        public DBService(IDbConnectionFactory connectionFactory) {
            this.connectionFactory = connectionFactory;
        }
    
        public IDbConnectionFactory DBFactory { get { return connectionFactory; } }
    
        public List<Customer> GetAllCustomers() {
            try {
                using (var connection = DBFactory.OpenDbConnection()) {
                    var dbResult = connection.Select<Customer>();
                    //... code omitted for brevity
                }
            } catch (Exception e) {
                //... code omitted for brevity
            }
        }
    }
    

    Select&lt;T&gt; 和 OpenDbConnection 看起来都像扩展方法。我建议检查他们的期望并模拟这些行为。

    如果DbService 本身被用作其他类的依赖项,那么该类也应该被抽象。

    public interface IDbService {
        IDbConnectionFactory DBFactory { get; }
        List<Customer> GetAllCustomers();
    }
    

    并让实现继承

    public class DbService : IDbService { 
        //... code removed for brevity
    }
    

    并确保使用 IoC 容器注册所有内容。

    【讨论】:

    • 是的,我同意,服务定位器反模式是我应该尽量避免的。我只是认为现在有一种更简单的方法,无需太多重构。
    • Technical debt 就是这样。它总是要求贷款。
    猜你喜欢
    • 1970-01-01
    • 2013-09-30
    • 2010-11-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-02
    相关资源
    最近更新 更多