سؤال

I am trying to use learn how to use FakeItEasy, and wanted to try using it with some data access code from an old project I have access to. While the basics of FIE seemed pretty easy, and I was able to get simple cases working, this one has me stumped.

The system used Entity Framework, and one of the data management classes handles Users, and I'm trying to figure out how to test just the basic GetUserByUserNumber function. I can use a fake IPersistenceManager<User> when instantiating the UserDataManager class, and then call the GetUserByUserNumber method, but the assertion to check that userPersistenceManager.ReadCustom was called always fails.

I've tried calling the Delete method on the fake userPersistenceManager, and the assertion for that works fine. I think it has something to do with the Linq Expression that the ReadCustom method takes as its first parameter. I just don't know how that should be handled. Any help with this would be appreciated!

This is the method in the UserDataManager that I'm trying to test:

public User GetUserByUserNumber(string userNumber, bool loadRelatedRecords = false)
{
    if (string.IsNullOrWhiteSpace(userNumber))
    {
        throw MyAppExceptions.CreateMyAppFatalException(Constants.ExceptionKeys.Unexpected, new ArgumentNullException("userNumber"));
    }
    Logger.Write(string.Format("Executing GetUserByUserNumber with UserNumber {0}.", userNumber), LogCategory.General, TraceEventType.Verbose);
    return _UserPersistenceManager.ReadCustom(mem => mem.UserNumber == userNumber, EntityConstants.EntityNames.UserDetail);
}

This is the IPersistenceManager method that I want to ensure is called:

TEntity ReadCustom(Expression<Func<TEntity, bool>> predicate, string includeEntityName);

This is my unit test:

[TestMethod]
public void GetUserByUserNumber_Calls_ReadCustom()
{
    // Arrange
    var userPersistenceManager = A.Fake<IPersistenceManager<User>>();
    var dataManager = new UserDataManager(userPersistenceManager);

    // Act
    dataManager.GetUserByUserNumber("123456", false);

    // Assert
    A.CallTo(() => userPersistenceManager.ReadCustom(u => u.UserNumber == "123456", EntityConstants.EntityNames.UserDetail)).MustHaveHappened();
}
هل كانت مفيدة؟

المحلول

I think Tim Long's answer is essentially correct although my slant is not that this is a failing of mocking frameworks—it comes down to how easy it is (in general, not just when mocking) to determine whether two things are "the same".

The problem you have is that unless told otherwise, FakeItEasy uses .Equals to compare the arguments. Expressions don't compare well with .Equals, and so you'll get a mismatch. One option is to explore Expression equality-checkers. There are a number of questions on StackOverflow about this already, such as How to check if two Expression<Func<T, bool>> are the same. If you can find a good way to determine the equality of the expressions, I think you could provide that method to FakeItEasy's argument matcher
(e.g. with A<Expression<Func<TEntity, bool>>.That.Matches(…)).

Alternatively, you can go Mr. Long's route and capture the argument and then interrogate it later. I suggested a similar approach just a bit ago when answering How to fake an action<> with FakeItEasy.

In your case, you could capture the predicate and then verify its correctness by seeing how it reacts to various input objects - does it like ones with UserNumber "123456".

نصائح أخرى

This seems to be an area of mocking frameworks that is very counter-intuitive and hard to use correctly. I generally shy away from doing argument matching and try to return or capture some sort of object that I can later make assertions against.

In your case, you are essentially comparing two expressions for equality. The 'look' different in the code even though they have the same syntax. I wonder, can you create those two expressions outside of the test context and see if they compare equal then?

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top