1

i've started to get my hands on mocking frameworks because I want to write some unit tests for methods which (indirectly) write something to a database. Here's a small example of the classes structure:

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

What I want to do now, is test the public persist method. The problem here is, that my DatabaseObject will save the data to an database. It would be easy to mock the DatabaseObject's save method, but I'm not sure what's the best way to inject the mocked object (as you can see, I don't have any knowledge of the DatabaseObject in my public method) into the PersistImpl method. One thougt was to pull up the DatabaseObject creation to the public method. So something like this:

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

But I'm quite unhappy with this soultion. I would need to create the database object outside the persistence class.

Is there are good way to solve such a problem? I definitly have to remove the database access within my unit tests.

4

1 回答 1

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返回的 a FakeDatabaseObject,它不会访问你的数据库。

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

于 2013-09-12T12:43:50.550 回答