如何在C#中单元测试NpgsqlDataReader扩展

时间:2016-01-29 08:22:17

标签: c# unit-testing npgsql

我一直在玩一些受Rob Conery的PostgreSQL Pluralsight文章启发的ADO.NET扩展。

我是单元测试的新手,并且已经尝试通过围绕我为NpgsqlDataReader类编写的几个扩展方法编写一些测试来学习更多相关内容。

我正在尝试测试的方法示例:

    public static T ToEntity<T>(this NpgsqlDataReader reader) where T : new()
    {
        var result = new T();
        var properties = typeof(T).GetProperties();

        foreach (var prop in properties)
        {
            for (var i = 0; i < reader.FieldCount; i++)
            {
                if (reader.GetName(i).Replace("_", "").Equals(prop.Name, StringComparison.InvariantCultureIgnoreCase))
                {
                    var val = reader.GetValue(i) != DBNull.Value ? reader.GetValue(i) : null;
                    prop.SetValue(result, val);
                }
            }
        }

        return result;
    }

我不确定如何开始模拟数据库连接,因为我想要测试的是迭代返回的结果对象并将列映射到通用实体类的属性的功能。

如何在不访问数据库的情况下测试实体映射?

由于

更新

我用来调用上面扩展名的代码是NpgsqlCommand类的另一个扩展:

public static IEnumerable<T> ToList<T>(this NpgsqlCommand cmd) where T : new()
{
    var results = new List<T>();
    var reader = cmd.ExecuteReader();

    while (reader.Read())
    {
        results.Add(reader.ToEntity<T>());
    }

    reader.Close();
    reader.Dispose();

    return results;
}

我将使用下面答案中公布的相同技术来测试此方法。

1 个答案:

答案 0 :(得分:1)

首先,方法实现不依赖于NpgsqlDataReader的具体实现。因此,您可以将签名更改为:

public static T ToEntity<T>(this IDataReader reader) where T : new()

接下来,签名不合逻辑。数据加载器用于循环结果集并生成行的(具有0,1个或更多元素),而不是单个结果。因此,更符合逻辑的签名和实现将是:

public static IEnumerable<T> ToEntity<T>(this IDataReader reader) where T : new()
{
    var properties = typeof(T).GetProperties();

    while (reader.Read())
    {
        var result = new T();
        foreach (var prop in properties)
        {
            for (var i = 0; i < reader.FieldCount; i++)
            {
                if (reader.GetName(i).Replace("_", "").Equals(prop.Name, StringComparison.InvariantCultureIgnoreCase))
                {
                    var val = reader.GetValue(i) != DBNull.Value ? reader.GetValue(i) : null;
                    prop.SetValue(result, val);
                }
            }
        }

        yield return result;
    }
}

现在您可以提供IDataReader的虚假实现。要么你可以使用像NSubstitute这样的模拟框架,但是如果你是单元测试的新手,那么更简单的方法就是手动实现一个。例如,in this gist (incomplete implementation)我在内存中的通用列表中实现了IDataReader(以及一种便于使用的扩展方法)。

现在你可以编写一个这样的测试方法:

// note the anonymous type here
var expectedEntity = new {MY_PROPERTY = "SomeValue"};
var reader = new[]
{
    expectedEntity
}.AsDataReader();
var entity = reader.ToEntity<MyEntity>().First();
Assert.AreEqual("SomeValue", entity.MyProperty);

最后,使用反射的解决方案有效,但最终可能会变慢。请记住,这种代码是一个已解决的问题;已经使用了轻量级的ORM,例如PetaPoco,dapper.net a.o。