【问题标题】:How to UnitTest a Function in a mocked method如何在模拟方法中对函数进行单元测试
【发布时间】:2015-04-17 05:38:52
【问题描述】:

如何在此处测试 DeleteAppointmentById?

 Func<IDataAdapterRW, IEnumerable<uint>> function = db =>   DeleteAppointmentById(db, appointmentId);

 return _dataContextProvider.GetContextRW().Run(function);

_dataContextProvider 是用 moq 模拟的。如果我运行测试,它当然永远不会进入 DeleteAppointmentById

测试方法:

public IEnumerable<uint> DeleteAppointment(uint appointmentId)
    {
        Func<IDataAdapterRW, IEnumerable<uint>> function = db => DeleteAppointmentById(db, appointmentId);
        return _dataContextProvider.GetContextRW().Run(function);
    }

DeleteAppointmentById 是我真正感兴趣的内部方法(私有)。

我的测试:

[Test]
    public void DeleteAppointment_Valid_DeletedRecordId()
    {
        //Setup
        var dbContextMock = new Mock<IDataContextProvider>();
        var dataAdapterMock = new Mock<IDataContext<IDataAdapterRW>>();

        dbContextMock.Setup(d => d.GetContextRW())
            .Returns(dataAdapterMock.Object);

        dataAdapterMock.Setup(a => a.Run(It.IsAny<Action<IDataAdapterRW>>()));
        var calendarService = new CalendarService(dbContextMock.Object);

        //Run
        var result = calendarService.DeleteAppointment(1);

        //Assert
        Assert.AreEqual(1, result);
    }

【问题讨论】:

  • 在这种情况下这几乎是不可能回答的,因为所有重要信息都丢失了(你嘲笑的dataContextProviderGetContextRW() 返回什么?这又被嘲笑了吗?你期望什么?应该DeleteAppointmentById所有一些方法还是你想检查行为(如@Luke)试图回答?
  • 您能解释一下您要达到的目标吗?从你所说的很难做出什么。
  • 对不起,我更新了问题,希望现在更清楚.. DeleteAppointmentById 中包含很多与数据上下文无关的逻辑。我想模拟所有数据上下文,并在该私有方法中测试逻辑。
  • hmmm...几天前您没有问过几乎相同的问题吗? - 是的,就在昨天:stackoverflow.com/questions/29670372/… - 我投票关闭那里的问题,因为你在这里提供了更多信息 - 但请不要多次发布相同的问题 - 谢谢
  • 再次:您想检查您的DeleteAppointmentById 是否在IDataAdapterRW 上调用了某个方法,还是要检查行为?在第一种情况下,如果在您的dataAdapterMock 上进行了正确的调用,您只需要Verify - 在第二种情况下,您应该模拟IDataAdapterRW,以便您可以跟踪项目的插入和移除并执行@Luke 在下面提出了什么(但不是在真正的 DB IMO 上)

标签: c# unit-testing moq


【解决方案1】:

您可以将Funcaccess the result 作为Run 方法中的参数传递,并断言 如下所示的结果。

为什么要返回结果?因为它是一个模拟,不知道Run 方法表现如何

[Test]
public void DeleteAppointment_Valid_DeletedRecordId()
{
    //Setup
    var dbContextMock = new Mock<IDataContextProvider>();
    var dataAdapterMock = new Mock<IDataContext<IDataAdapterRW>>();

    dbContextMock.Setup(d => d.GetContextRW())
        .Returns(dataAdapterMock.Object);

    dataAdapterMock.Setup(a => a.Run(It.IsAny<Func<IDataAdapterRW, IEnumerable<uint>>>()))
                   .Returns((Func<IDataAdapterRW, IEnumerable<uint>> func) => { return func(dataAdapterMock.Object);}); // configure the mock to return the list
    var calendarService = new CalendarService(dbContextMock.Object);

    //Run
    int id = 1;
    var result = calendarService.DeleteAppointment(id);

    //Assert
    var isInList = result.Contains(id); // verify the result if contains the
    Assert.AreEqual(isInList, true);
}

【讨论】:

  • 这只是测试它自己的模拟......我认为OP想要测试DeleteAppointmentById......但如果你添加几行你仍然可以管理......
  • @CarstenKönig 你是对的,我修改了Return 以给出Run 方法中传递的参数 的结果。
  • ;) - 好的,但现在你必须以某种方式告诉模拟者 is 首先有一个带有id = 1 的条目,我猜在DeleteAppointment 之后它不应该不再存在;) ...你看到这种测试比实际代码更脆弱 - 这就是为什么我永远不会写这样的东西(不是你的错) - 但你仍然必须安排事实第一;9
  • @CarstenKönig 你说得对,实际的Test 已经结束了DeleteAppointmentById 方法,我们不知道该方法的主体dataAdapterMock 可能需要一些配置,如你所说,返回id = 1
  • 你们绝对是天才。谢谢,就是这样。
【解决方案2】:

单元测试倾向于采用以下结构:

  • 安排:设置上下文。在这种情况下,您可能会创建一个约会并将其保存到数据库中。

  • Act:调用您正在测试的单元。在这种情况下,DeleteAppointmentById(db, 约会)。

  • 断言:检查副作用和返回是否正确。在这种情况下,您可以尝试从数据库中加载此约会,并断言您无法(因为它应该已被删除)。

【讨论】:

  • 如果您选择这样做(尽管在这种情况下我不会将其称为 unit-test),但请确保清洁在finally 块中建立您的数据库 - 顺便说一句:我根本不知道这是如何回答这个问题的,但似乎有人这样做了
  • 对不起,如果我不够清楚,我知道如何进行单元测试,但在这种特殊情况下有问题。我更新了我的描述
猜你喜欢
  • 2017-02-03
  • 1970-01-01
  • 1970-01-01
  • 2019-08-22
  • 2019-03-23
  • 1970-01-01
  • 2019-03-19
  • 2018-04-04
  • 2021-09-27
相关资源
最近更新 更多