【问题标题】:LINQ to Entities error occurring at run time but not in unit testLINQ to Entities 错误发生在运行时但不在单元测试中
【发布时间】:2023-04-03 19:21:01
【问题描述】:

我正在我的一个控制器中对一种方法进行单元测试,以检查 Linq 查询是否返回了正确的数据(这可能是一个集成测试,但问题仍然存在)。

我试图通过在测试中复制运行时遇到的任何错误,然后在代码中修复它,尽可能地遵循 TTD。

但是,我遇到了一个错误,该错误仅在我的应用程序的运行时执行期间发生而不是在我的测试期间发生。

我在运行时遇到的异常是:

抛出异常:Newtonsoft.Json.dll 中的“System.NotSupportedException”

附加信息:LINQ to Entities 无法识别方法“System.String ToLowerInvariant()”方法,并且该方法无法转换为存储表达式。

被测方法为:

[HttpGet]
public ActionResult GetRegions(string term)
{
    var regions = from region in DbContext.Culture
                  where
                      !region.NeutralCultureFlag &&
                      (term == null || (region.NativeName + region.CultureCode).ToLowerInvariant().Contains(term.ToLowerInvariant()))
                  orderby region.Name
                  select region;

    return Json(regions.Select(x => new
                {
                    id = x.CultureId,
                    text = x.NativeName + " (" + x.CultureCode + ")"
                })
    );
}

该错误的修复显然是将(region.NativeName + region.CultureCode).ToLowerInvariant() 更改为(region.NativeName.ToLowerInvariant() + region.CultureCode.ToLowerInvariant()),但我想在修复它之前在我的测试中得到这个错误。

我的测试方法是:

public void Test1(string term, int expectedCount)
{
    var result = _controller.GetRegions(term);

    Assert.IsType(typeof (JsonNetResult), result);
    var jsonResult = (JsonNetResult)result;

    var data = GetJsonObject<List<JsonData>>(jsonResult);
}

JsonNetResult 是一个自定义类,它扩展了普通的JsonResult,因此我可以使用 Json.Net 而不是标准的 Json 序列化器。

JsonNetResult 中的ExecuteResult 方法看起来像:

public override void ExecuteResult(ControllerContext context)
{
    if (context == null)
        throw new ArgumentNullException("context");

    HttpResponseBase response = context.HttpContext.Response;
    response.ContentType = string.IsNullOrEmpty(this.ContentType) ? "application/json" : this.ContentType;

    if (this.ContentEncoding != null)
        response.ContentEncoding = this.ContentEncoding;
    if (this.Data == null)
        return;

    var scriptSerializer = JsonSerializer.Create(this.Settings);

    using (var sw = new StringWriter())
    {
        scriptSerializer.Serialize(sw, this.Data); //error occurs here
        response.Write(sw.ToString());
    }
}

异常发生在scriptSerializer.Serialize(sw, this.Data);

有没有办法在我的单元测试中复制错误,以便我可以确保它按照 TTD 原则正确修复?

【问题讨论】:

  • 您的测试是否连接到与您的生产相同类型的数据库?
  • 我的测试设置为使用模拟的DbContext
  • 在这种情况下,正确测试它的唯一方法是拥有一个连接到测试数据库的非模拟上下文 - 因为该错误专门在 @ 987654332@ -> SQL statement 发电机

标签: c# json linq unit-testing


【解决方案1】:

因为您的单元测试使用模拟上下文,所以您将获得 LINQ to objects 而不是 LINQ to entity。

linq to entities vs linq to objects - are they the same?

从上面的链接:

LINQ-to-Objects 是 IEnumerable 上的一组扩展方法, 允许您对任意序列执行内存查询操作 的对象。这些方法在必要时接受简单的委托。

LINQ-to-Entities 是一个 LINQ 提供程序,具有一组扩展 IQueryable 上的方法。这些方法构建了一个表达式树 (这就是为什么委托实际上是作为 Expressions 传递的),并且 提供者将根据其解析构建一个 SQL 查询 表达式树。

例如,考虑以下查询:

 var query1 = mydb.MyEntity.Select(x => x.SomeProp).Where(x => x ==> "Prop");
 var query2 = mydb.MyEntity.Select(x =>> x.SomeProp).AsEnumerable().Where(x => x == "Prop"); 

第一个查询是 将建立一个由 select 和 where 组成的表达式树, 两个 lambdas 实际上被认为是 LambdaExpressions。这 LINQ-to-Entities 提供程序会将其转换为 SQL 选择和过滤。

第二个查询插入一个 AsEnumerable(),这将强制 查询的其余部分使用 LINQ-to-Objects。在这种情况下, provider 将仅根据选择生成 SQL,返回所有 那些来自数据库的记录,然后过滤将发生 在记忆中。显然,这可能会慢得多。

【讨论】:

  • 虽然只有链接的答案通常不受欢迎,但我会将其标记为已接受,因为它是正确的。对于未来的访问者,重现错误将涉及使用真实数据库(可能在内存中)进行集成测试。
  • 是的,以防链接失效或源更改。
猜你喜欢
  • 2011-09-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-06-26
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多