简体   繁体   English

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

[英]How to unit test NpgsqlDataReader extensions in C#

I've been playing around with some ADO.NET extensions inspired by a PostgreSQL Pluralsight article by Rob Conery. 我一直在玩一些受Rob Conery的PostgreSQL Pluralsight文章启发的ADO.NET扩展。

I'm new to unit testing and have been trying to learn some more about it by writing some tests around a couple of extension methods I've written for the NpgsqlDataReader class. 我是单元测试的新手,并且一直在通过围绕为NpgsqlDataReader类编写的几种扩展方法编写一些测试来尝试了解更多有关它的信息。

An example of a method I'm trying to test: 我尝试测试的方法的一个示例:

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

I'm not sure how to even start mocking the database connection, as all I want to test is functionality of iterating over the returned result object and mapping the columns to the properties of a generic entity class. 我不确定如何开始模拟数据库连接,因为我要测试的只是迭代返回的结果对象并将列映射到通用实体类的属性的功能。

How would I go about testing the entity mapping, without accessing the database? 我将如何在不访问数据库的情况下测试实体映射?

Thanks 谢谢

Update: 更新:

The code I've been using to call the extension above is another extension on the NpgsqlCommand class: 我用来调用上述扩展的代码是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;
}

I'll be using the same techniques posted in the answer below to test this method as well. 我还将使用下面答案中发布的相同技术来测试此方法。

First of all, the method implementation does not depend on the concrete implementation of NpgsqlDataReader. 首先,该方法的实现不依赖于NpgsqlDataReader的具体实现。 So you can change the signature to: 因此,您可以将签名更改为:

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

Next, the signature is not logical. 其次,签名不合逻辑。 A datareader is used to loop over a result set and yields a list of rows (with 0, 1 or more elements), not a single result. 数据读取器用于循环结果集,并产生行列表 (具有0、1或多个元素),而不是单个结果。 So a more logical signature and implementation would be: 因此,更合理的签名和实现将是:

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

Now you can provide a fake implementation of IDataReader. 现在,您可以提供IDataReader的虚假实现。 Either you can use a mocking framework such as NSubstitute, but if you're new to unit testing a simpler way is to manually implement one. 可以使用诸如NSubstitute之类的模拟框架,但是,如果您不熟悉单元测试,则一种更简单的方法是手动实现。 For example, in this gist (incomplete implementation) I implemented IDataReader on an in-memory generic list (and an extension method to facilitate usage). 例如, 在这个要点(不完整的实现)中,我在内存中的通用列表上实现了IDataReader(以及用于方便使用的扩展方法)。

Now you can write a test method like this: 现在您可以编写这样的测试方法:

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

Finally, your solution using reflection works, but may end up rather slow. 最终,使用反射的解决方案可行,但最终可能会很慢。 Keep in mind that this kind of code is a solved problem; 请记住,这种代码是可以解决的问题。 have alook at light-weight ORM's such as PetaPoco, dapper.net ao 看看轻量级的ORM,例如PetaPoco,dapper.net ao

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM