FakeItEasy throws ExpectationException

I have an issue mocking an interface with an async method. The interface looks like this:

public interface IDataAccessLayer
    {
        Task<bool> ExistsUserAsync(string username, CancellationToken cancellationToken);

        Task<IUser> CreateUserAsync(string username, string password, DateTime dateOfBirth, CancellationToken cancellationToken);
    }

      

.. the cancelationToken up parameter is always created and passed externally at runtime (via NancyFx) and on mocking calls inside the test method like this:

    var validSignupRequest = new UserSignupRequest()
    {
        Username = "meh-spacey_space",
        DateOfBirth = DateTime.Now.Subtract(TimeSpan.FromDays(365*35)),
        Password = "someproper_passw*rd"
    };

    var testDataAccessLayer = A.Fake<IDataAccessLayer>(options => options.Strict());

    var fakeUserInstance = A.Fake<IUser>();

    A.CallTo(() => fakeUserInstance.Username).Returns(validSignupRequest.Username);
    A.CallTo(() => testDataAccessLayer.ExistsUserAsync(validSignupRequest.Username, CancellationToken.None)).Returns(false); // works
    A.CallTo(() => testDataAccessLayer.CreateUserAsync(validSignupRequest.Username, validSignupRequest.Password, validSignupRequest.DateOfBirth, CancellationToken.None)).Returns(fakeUserInstance); // does not work / throws ExpectationException

      

.. the mocked call .ExistsUserAsync (...) does work, the .CreateUserAsync file does not work / throws an ExpectationException.

I checked the underlying code that is being tested and the call is made using the same values โ€‹โ€‹as the .CreateUserAsync (...) method and I suspected dateOfBirth was the culprit, but at least. wise he is exactly the same.

I have a bit of doubt about how FakeItEasy does the signature / parameter value matching, because in theory, that causes mockery, but FakeItEasy says it doesn't. So far it wins.

Does anyone know what's wrong with my mocking calling method?

+3


source to share


2 answers


To have an "answer" to this question, so that newbies don't have to go through comments and judgments, and not yet understand, here is my understanding of what happened:

In the original question, the call

A.CallTo(() => testDataAccessLayer.CreateUserAsync(
                         validSignupRequest.Username,
                         validSignupRequest.Password,
                         validSignupRequest.DateOfBirth,
                         CancellationToken.None))
               .Returns(fakeUserInstance); 

      



does not throw, but later when production code was produced and called testDataAccessLayer

, the configured method was not executed - FakeItEasy did not think the call matched the setting.

This is because between setting up the call and the actual call, it DateOfBirth

ends up serializing and deserializing the Jil as shown in this gist , and the DateTime loses precision, so the parameter is no longer the same. Thus, FakeItEasy does not see the need to intercept the call.

As noted by Jรถrg B. in the comments, there were no problems with FakeItEasy. He did his best using the data provided to him.

0


source


This is just a guess as I am not familiar with FakeItEasy, but perhaps you need to wait for the results of asynchronous calls.

A.CallTo(async () => await testDataAccessLayer.CreateUserAsync(   // await the result
                     validSignupRequest.Username, validSignupRequest.Password, validSignupRequest.DateOfBirth, CancellationToken.None))
    .Returns(fakeUserInstance);

      

Or, depending on how FakeItEasy works internally, maybe this is how you check the result:



A.CallTo(() => testDataAccessLayer.CreateUserAsync(
               validSignupRequest.Username, validSignupRequest.Password, validSignupRequest.DateOfBirth, CancellationToken.None))
    .Returns(Task.FromResult(fakeUserInstance)); // test a task, not a real object

      

A quick search found this page which relies on the second solution, but I think it will probably work.

0


source







All Articles