【问题标题】:How to mock Entity Framework and get the new Inserted value in the mocked context如何模拟实体框架并在模拟上下文中获取新的插入值
【发布时间】:2016-07-27 11:03:48
【问题描述】:

我在 n 层架构中模拟实体框架。我正在尝试模拟插入。插入有效,但是当我尝试获取插入实体的值时,我无法获得正确的值。

编辑

这是带有变量pat的测试用例。在这种情况下,测试失败是因为关于尝试日期的断言失败。

var mockContext = new Mock<PublicAreaContext>();

//Here I mock the entity I cannot get the correct values
PaymentAttemptTrace pat = new PaymentAttemptTrace();
var mockSetPaymentAttemptTrace = new Mock<DbSet<PaymentAttemptTrace>>();
mockContext.Setup(m => m.Set<PaymentAttemptTrace>()).Returns(mockSetPaymentAttemptTrace.Object);

//Here I create a fake request 
TracePaymentAttemptRequest request = new TracePaymentAttemptRequest();
... 

//I call the facade. The facade create a PaymentAttemptTrace and insert it in the mocked db
ToolsFacade facade = new ToolsFacade(mockContext.Object);
TracePaymentAttemptResponse response = facade.TraceAutoPayPaymentAttempt(request);

//All asserts are ok, except the last one. The date remain "empty", even if is valorized correctly during the execution of the code (I have checked in debug)
Assert.IsTrue(response.Result == it.MC.WebApi.Models.ResponseDTO.ResponseResult.Success);

mockSetPaymentAttemptTrace.Verify(m => m.Add(It.IsAny<PaymentAttemptTrace>()), Times.Once());
mockContext.Verify(m => m.SaveChanges(), Times.Once());

Assert.IsTrue(pat.AttemptDate == new DateTime(2016, 07, 27, 11, 46, 24));

这是没有变量pat的测试用例。在这种情况下,测试失败(当然)因为我没有嘲笑实体!!!

var mockContext = new Mock<PublicAreaContext>();

//Here I create a fake request 
TracePaymentAttemptRequest request = new TracePaymentAttemptRequest();
... 

//I call the facade. The facade create a PaymentAttemptTrace and insert it in the mocked db
ToolsFacade facade = new ToolsFacade(mockContext.Object);
TracePaymentAttemptResponse response = facade.TraceAutoPayPaymentAttempt(request);

Assert.IsTrue(response.Result == it.MC.WebApi.Models.ResponseDTO.ResponseResult.Success);

mockSetPaymentAttemptTrace.Verify(m => m.Add(It.IsAny<PaymentAttemptTrace>()), Times.Once());
mockContext.Verify(m => m.SaveChanges(), Times.Once());

这里是我要测试的代码:

public TracePaymentAttemptResponse TraceAutoPayPaymentAttempt(TracePaymentAttemptRequest request)
{
    ...
    DateTime attemptDate = DateTime.Now.Date;
    if (!string.IsNullOrWhiteSpace(request.DataTentativoPagamento))
    {
        try
        {
            attemptDate = DateTime.ParseExact(request.DataTentativoPagamento, "yyyyMMddTHHmmss", System.Globalization.CultureInfo.InvariantCulture);
        }
        catch (Exception) { /* Do nothing. attemptDate = DateTime.Now.Date; */ }
    }

    PaymentAttemptTrace trace = this.CreatePaymentAttemptTraceEntity(/* All data I need to create my entity */);

    Repository<PaymentAttemptTrace> repository = new Repository<PaymentAttemptTrace>(base.Context);
    repository.Insert(trace); // <- If not mock the entity pat, here go in exception!!

    repository.SaveChanges();

    ...
}

所以我必须模拟 pat 变量,即使我不在测试中使用它!我测试的目的是验证尝试日期的解析是否正确。

可能是什么问题?我想念什么?

谢谢

再次编辑 我让你看另一个测试。这个测试有效!在这个测试中,我必须更新一个实体:

var mockContext = new Mock<PublicAreaContext>() { CallBase = true };

List<BillingCenter> billingCenters = new List<BillingCenter>()
{
    new BillingCenter() { Id = "12345600", CustomerId = "123456", PaymentMethod = PaymentMethod.Easypay }
};

var data = billingCenters.AsQueryable();

var mockSet = new Mock<DbSet<T>>();
mockSet.As<IQueryable<T>>().Setup(m => m.Provider).Returns(data.Provider);
mockSet.As<IQueryable<T>>().Setup(m => m.Expression).Returns(data.Expression);
mockSet.As<IQueryable<T>>().Setup(m => m.ElementType).Returns(data.ElementType);
mockSet.As<IQueryable<T>>().Setup(m => m.GetEnumerator()).Returns(data.GetEnumerator());

mockContext.Setup(m => m.Set<BillingCenter>()).Returns(mockSet.Object);

mockContext.Setup(x => x.SaveChanges()).Returns(1);

//Here I create a request
UpdateEasyPayFromResultPaymentRequest request = new UpdateEasyPayFromResultPaymentRequest();
...

PublicAreaFacade facade = new PublicAreaFacade(mockContext.Object);
UpdateEasyPayFromResultPaymentResponse response = facade.UpdateEasyPayFromResultPayment(request);

Assert.IsTrue(billingCenters[0].PaymentMethod == PaymentMethod.Autopay);

如您所见,我用paymentMethod = Easypay 创建了billingCenter。在测试结束时,我做了一个断言来检查Autopay 中计费中心的付款方式是否发生了变化。但是我不改变测试里面的值!我在 facade.UpdateEasyPayFromResultPayment 方法中更改它

【问题讨论】:

  • 关于日期设置的预期行为是什么。除了您的断言之外,在您的示例中没有看到 pat 在任何地方被使用/调用,所以我怀疑除非日期是在类内部设置的,否则它应该保持默认
  • 我看到了更新。您还没有显示 pat 设置在您的测试中使用的位置。在您的示例中,pat 只是被实例化,然后在最后被断言。它没有被传递给任何东西。同样在更新的代码中,日期没有被分配给任何东西,所以甚至看不到您设置日期的位置。你的例子是不完整的
  • 请提供minimal reproducible example,以便那些试图帮助您的人重现您的问题。
  • 那是完整的例子......所以问题是我没有在任何地方使用pat......但是我不明白......我该如何测试插入在 Facade 内创建的实体?但是,如果我评论 pat 测试会抛出异常,因为我没有模拟实体 PaymentAttemptTrace
  • 如果是这种情况,那么您还没有展示完整的示例。如果您删除 pat 那么您甚至应该无法编译,因为您在下面的断言中使用了它。

标签: c# entity-framework mocking integration-testing moq


【解决方案1】:

这是您已经发现的另一种解决方案

//ARRANGE
bool patAdded = false;
PaymentAttemptTrace pat = null; //will assign a value to this when adding new entity

var mockSetPaymentAttemptTrace = new Mock<DbSet<PaymentAttemptTrace>>();
//setup a call back on Add to get the entity that was added to dbset
mockSetPaymentAttemptTrace
    .Setup(m => m.Add(It.IsAny<PaymentAttemptTrace>()))
    .Callback((PaymentAttemptTrace arg) => {
        pat = arg;
        padAdded = (pat != null);
    });

var mockContext = new Mock<PublicAreaContext>();
mockContext.Setup(m => m.Set<PaymentAttemptTrace>()).Returns(mockSetPaymentAttemptTrace.Object);
mockContext.Setup(x => x.SaveChanges()).Returns(1);//for when you save the added entity

//Here I create a fake request 
TracePaymentAttemptRequest request = new TracePaymentAttemptRequest();

... 

//I call the facade. The facade create a PaymentAttemptTrace and insert it in the mocked db
ToolsFacade facade = new ToolsFacade(mockContext.Object);

//ACT
TracePaymentAttemptResponse response = facade.TraceAutoPayPaymentAttempt(request);

//ASSERT
Assert.IsTrue(response.Result == it.MC.WebApi.Models.ResponseDTO.ResponseResult.Success);

mockSetPaymentAttemptTrace.Verify(m => m.Add(It.IsAny<PaymentAttemptTrace>()), Times.Once());
mockContext.Verify(m => m.SaveChanges(), Times.Once());

Assert.IsTrue(patAdded);
Assert.IsTrue(pat.AttemptDate == new DateTime(2016, 07, 27, 11, 46, 24));

【讨论】:

  • 不,我很抱歉......我比我更喜欢你的解决方案,但 pat 仍然为空
  • @Ciccio 如果您发布的答案有效,那么我的答案中的pat 应该在您的代码尝试将新实体添加到数据库集时设置。
  • 我不知道...也许我必须在 Callback 函数内返回?但是,当我调试 args 时,它的价值是正确的
  • 你不应该这样做。如果在调试其他内容时正确填充了args,则可能会将pat 重置为null
  • 我已经一步一步完成了,我没有设置为null对象...似乎一切都很好,直到我回到测试方法
【解决方案2】:

我是这样解决的

var mockContext = new Mock<PublicAreaContext>();

//Here I mock the entity I cannot get the correct values
List<PaymentAttemptTrace> pat = new List<PaymentAttemptTrace>();
var mockSetPaymentAttemptTrace = new Mock<DbSet<PaymentAttemptTrace>>();
mockSetPaymentAttemptTrace.Setup(m => m.Add(It.IsAny<PaymentAttemptTrace>())).Callback<PaymentAttemptTrace>(list.Add);
mockContext.Setup(m => m.Set<PaymentAttemptTrace>()).Returns(mockSetPaymentAttemptTrace.Object);

mockContext.Setup(x => x.SaveChanges()).Returns(1);

//Here I create a fake request 
TracePaymentAttemptRequest request = new TracePaymentAttemptRequest();
... 

//I call the facade. The facade create a PaymentAttemptTrace and insert it in the mocked db
ToolsFacade facade = new ToolsFacade(mockContext.Object);
TracePaymentAttemptResponse response = facade.TraceAutoPayPaymentAttempt(request);

//All asserts are ok, except the last one. The date remain "empty", even if is valorized correctly during the execution of the code (I have checked in debug)
Assert.IsTrue(response.Result == it.MC.WebApi.Models.ResponseDTO.ResponseResult.Success);

mockSetPaymentAttemptTrace.Verify(m => m.Add(It.IsAny<PaymentAttemptTrace>()), Times.Once());
mockContext.Verify(m => m.SaveChanges(), Times.Once());

Assert.IsTrue(pat[0].AttemptDate == new DateTime(2016, 07, 27, 11, 46, 24));

这里的差异与我的问题相比:

List<PaymentAttemptTrace> pat = new List<PaymentAttemptTrace>();
var mockSetPaymentAttemptTrace = new Mock<DbSet<PaymentAttemptTrace>>();
mockSetPaymentAttemptTrace.Setup(m => m.Add(It.IsAny<PaymentAttemptTrace>())).Callback<PaymentAttemptTrace>(list.Add);

还有

mockContext.Setup(x => x.SaveChanges()).Returns(1);

我不喜欢这个解决方案,只是......但它有效!我等待更好的东西!谢谢

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-05-13
    • 2016-12-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-05-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多