【问题标题】:Mock database object which is called in private method在私有方法中调用的模拟数据库对象
【发布时间】:2013-09-12 21:08:36
【问题描述】:

我开始接触模拟框架,因为我想为(间接)向数据库写入内容的方法编写一些单元测试。 这是类结构的一个小例子:

class SomePersistenceClass
{
    public void Persist(object value, string type)
    {
        if (type.Equals("Numeric"))
        {
            PersistNumeric( (int)value );
        }
    }

    private void PersistNumeric(int number)
    {
        //Some Calculations and Creation of DBObject
        number++;
        var dbObject = new DatabaseObject {DbObjectData = new DatabaseObjectData {Number = number, Creator = "John Doe"}};
        PersistImpl(dbObject);
    }

    private void PersistImpl(IDatabaseObject dbObject)
    {
        try
        {
            dbObject.Save();
        }
        catch (Exception)
        {
            //ErrorHandling
            throw;
        }
    }
}

interface IDatabaseObject
{
    DatabaseObjectData DbObjectData { get; set; }
    void Save();
}

class DatabaseObject : IDatabaseObject
{
    public DatabaseObjectData DbObjectData { get; set; }

    public void Save()
    {
        //Save to Database;
    }
}

class DatabaseObjectData
{
    public int Number { get; set; }
    public string Text { get; set; }
    public string Creator { get; set; }
}

我现在想做的是测试公共持久化方法。这里的问题是,我的 DatabaseObject 会将数据保存到数据库中。模拟 DatabaseObject 的保存方法很容易,但我不确定将模拟对象注入 PersistImpl 的最佳方法是什么(如您所见,我对公共方法中的 DatabaseObject 没有任何了解)方法。一个想法是将 DatabaseObject 创建拉到公共方法。 所以是这样的:

    public void Persist(object value, string type, IDatabaseObject dbObject)
    {
        if (type.Equals("Numeric"))
        {
            PersistNumeric( (int)value, dbObject );
        }
    }

    private void PersistNumeric(int number, IDatabaseObject dbObject)
    {
        //Some Calculations and Creation of DBObject
        number++;
        dbObject.DbObjectData.Number = number;
        dbObject.DbObjectData.Creator = "John Doe";
        PersistImpl(dbObject);
    }

但是我对这个灵魂很不满意。我需要在持久性类之外创建数据库对象。

有没有好办法解决这样的问题?我绝对必须在我的单元测试中删除数据库访问权限。

【问题讨论】:

  • 为什么不在 SomePersistenceClass 的构造函数中注入一个 mock IDatabaseObject?
  • 这是我现在正在考虑的事情。问题是,我不能使用相同的 DatabaseObject 而只是替换数据。有一些具有状态的数据库对象,所以我需要为每个要保存的对象创建一个自己的 DatabaseObject。
  • 很公平,但都一样,哇。这听起来像是一个非常奇怪的设计 :) 在这种情况下,尽管下面的工厂模式肯定是你需要的。

标签: c# unit-testing mocking


【解决方案1】:

解决问题的一种方法是将DatabaseObject 对象的构造委托给工厂类:

interface IDatabaseObjectFactory
{
    IDatabaseObject Create(DatabaseObjectData data);
}

class DatabaseObjectFactory : IDatabaseObjectFactory
{
    public IDatabaseObject Create(DatabaseObjectData data)
    {
        return new DatabaseObject {DbObjectData = data };
    }
}

class SomePersistenceClass
{
    readonly IDatabaseObjectFactory _factory;

    public SomePersistenceClass()
    {
        _factory = new DatabaseObjectFactory();
    }

    public SomePersistenceClass(IDatabaseObjectFactory factory)
    {
        _factory = factory;
    }

    public void Persist(object value, string type) { /* ... */ }
    private void PersistImpl(IDatabaseObject dbObject) { /* ... */ }

    private void PersistNumeric(int number)
    {
        //Some Calculations and Creation of DBObject
        number++;
        var dbObject = _factory.Create(new DatabaseObjectData {Number = number, Creator = "John Doe"});
        PersistImpl(dbObject);
    }

}

现在,如果你测试你的类,你可以传入一个FakeDatabaseObjectFactory,它返回一个FakeDatabaseObject,它不会访问你的数据库。

(但是,我发现您使用 DatabaseObject 类处理数据库访问的方式有点……奇怪;但这是另一个主题)。

【讨论】:

  • 这似乎是处理问题的好主意。我同意,DatabaseObject 类有点奇怪,但我不得不使用它。
猜你喜欢
  • 1970-01-01
  • 2018-05-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-05-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多