Simone
Simone

Reputation: 2430

How to mock Entity Framework and get the new Inserted value in the mocked context

I am mocking Entity Framework in a n-layer architecture. I am trying to mock an insert. The insert works, but when I try to get the values of the inserted entity, I cannot get the correct ones.

EDIT

This is the test case with the variable pat. In this case the test fails because the assert about the attempt date fails.

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

This is the test case without the variable pat. In this case the test fails (of course) because I have not mocked the entity!!!

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

Here the code I want to test:

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

    ...
}

So I have to mock the pat variable even if I do not use it in the test! The purpose of my test is verify if the parse of the attemptDate is correct.

What can be the problem? What I miss?

Thank you

EDIT AGAIN I let you see another test. This test works! In this test I have to do an update of an entity:

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

As you can see, I create the billingCenter with paymentMethod = Easypay.. At the end of the test I do an asserto to check if the payment method of billing center has changed in Autopay. But I do not change the value inside the test! I change it inside the facade.UpdateEasyPayFromResultPayment method

Upvotes: 3

Views: 3939

Answers (2)

Nkosi
Nkosi

Reputation: 247068

Here is another solution to what you already discovered

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

Upvotes: 3

Simone
Simone

Reputation: 2430

I have solved in this way

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

Here the differences compare to my question:

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

And

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

I do not like this solution, onestly... but it works!!! I wait for something better! Thank you

Upvotes: 1

Related Questions