代码之家  ›  专栏  ›  技术社区  ›  Stu Ratcliffe

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

  •  0
  • Stu Ratcliffe  · 技术社区  · 10 年前

    我一直在和一些ADO玩。NET扩展的灵感来自Rob Conery的一篇PostgreSQL Pluralsight文章。

    我是单元测试的新手,一直试图通过围绕我为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 回复  |  直到 10 年前
        1
  •  1
  •   jeroenh    10 年前

    首先,方法实现不依赖于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,整洁。净资产。