【问题标题】:How to unit test nested method using xunit in .net core如何在 .net 核心中使用 xunit 对嵌套方法进行单元测试
【发布时间】:2021-10-15 03:13:56
【问题描述】:

我有以下场景

public long Update(ExpenseViewModel updateModel, string einterpriseid)
{
    long internId;
    ExpenseReq expenseRequest = GetReqById(updateModel.Id);
    if (expenseRequest != null)
    {
        int DraftStatusID = GetStatusID("Draft");
        int PendingStatusID = GetStatusID("Pending Approval");
    }
 }

 public ExpenseReq GetReqById(int id)
 {
    return _db.ExpRequest
            .Include(x => x.Profile).AsNoTracking()
            .Include(x => x.ExpReqStatus).AsNoTracking()
            .Include(x => x.AdditionalDetails)
            .ThenInclude(x => x.ExpenseReqTravelDetails).AsNoTracking()
            .Include(x => x.AddDetail)
            .ThenInclude(x => x.ExpRejReason).AsNoTracking()
            .Include(x => x.AdditionalDetails)
            .ThenInclude(x => x.ExpenseTypes).AsNoTracking()
            .Include(x => x.AdditionalDetails)
            .ThenInclude(x => x.ExpReqApprover).AsNoTracking()
            .Include(x => x.Comp).AsNoTracking()
            .Where(x => x.Id == id).FirstOrDefault();
 }

当我对Update 执行单元测试时,GetReqById 函数总是返回我null,因为没有满足 if 条件并且内部 if 条件行覆盖没有发生。我想在 if 条件下获得代码覆盖率。如何为它编写单元测试? 我在下面尝试过:

[Fact]
public void Should_Update()
{
    var options = new DbContextOptionsBuilder<dbContextobj>().UseInMemoryDatabase(databaseName: "fakedb").Options;

    using (var context = new dbContextobj(options))
    { 
        var controller = new ExpenseRequestService(context);
        var result = controller.Update(expenseRequestUpdateViewModel, "test");
        Assert.Equal(0, result);
    }
}

这工作正常,但 GetReqById 没有被覆盖。请帮我提供一些示例代码。

谢谢。

【问题讨论】:

  • 如果您将GetReqById 移动到另一个类和接口中,然后将该接口注入到对象的构造函数中,那么您的测试代码可以“模拟”该接口以使该方法调用返回您想要的任何内容。
  • 感谢您的回复。这里的问题是代码已经在生产中运行,现在对现有代码进行任何更改都将进行彻底的测试和所需的批准。所以我不想对现有的进行任何更改,并且不进行任何更改我想执行单元测试。
  • 你使用UseInMemoryDatabase 意味着每次你运行项目时,据我所知,它都会创建新数据。因此,如果您真的要测试代码单元,您确实需要模拟数据库的返回,否则您将进行更多测试,那么您应该这样做。请记住,单元测试专注于测试方法而不是您的依赖项。
  • 与问题无关,但我在源代码中注意到了一些东西。我不相信你应该在每个Include 上使用AsNoTracking()AsNoTracking() 将应用于父实体和所有子实体:stackoverflow.com/questions/42002540/asnotracking-and-include
  • 虽然我理解您不愿做出对您需要添加的测试而言并非绝对必要的更改,但当开发人员害怕重构代码时,这对我来说是一个危险信号。只是说。

标签: c# .net unit-testing asp.net-core-webapi xunit


【解决方案1】:

您可以使用所需的数据设置数据库:

[Fact]
public void Should_Update()
{
    // Assign
    var options = new DbContextOptionsBuilder<dbContextobj>().UseInMemoryDatabase(databaseName: "fakedb").Options;
    using var context = new dbContextobj(options);
    context.ExpenseReq.Add(new ExpenseReq { Id = "test" })
    context.SaveChanges();

    var controller = new ExpenseRequestService(context);

    // Act
    var result = controller.Update(expenseRequestUpdateViewModel, "test");

    // Assert
    Assert.Equal(0, result);
}

但是,由于使用了InMemoryDatabase,很多人会评论说这在技术上不是一个单元测试。

【讨论】:

  • 嗨@peter Riesz,我已经尝试过上面的代码,但仍然面临同样的问题。我在我的问题部分添加了一个截图供参考,请看一下。我需要这个 GetReqById 应该返回一些对象,以便 if 条件通过,从而增加行代码覆盖率。
  • 对不起@ManishGupta 它为我工作。我做了一些实验,看看如果Id 使用ValueGenerators 或者Includes 1-1 需要关系但它仍然返回查询的实体,它是否会失败。
  • 你是对的。它可以工作,但前提是我保留第一个表,例如:- return _db.ExpRequest.Where(x => x.Id == id).FirstOrDefault(); .. 如果我包含所有其他表,则查询返回 null。不知道为什么..虽然我正在为其他表添加数据。
  • 它的工作。由于存在 FK 关系,因此只需要映射正确的数据。非常感谢。
猜你喜欢
  • 1970-01-01
  • 2021-09-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-10-20
  • 2020-01-31
  • 2022-11-11
相关资源
最近更新 更多