简体   繁体   中英

Mocking method that takes Func<T>

I'm trying to do examples in this article

There is a Generic repository that has a Get method like below.

public virtual TEntity Get(Func<TEntity, Boolean> where)
{
    return DbSet.Where(where).FirstOrDefault();
}

And a service method that Authenticate users by username and password that uses repositories Get.

public int Authenticate(string username, string password)
{
    var user = _unitOfWork.UserRepository.Get(u => u.UserName == username && u.Password == password);

    if (user != null && user.UserId > 0)
    {
        return user.UserId;
    }
    return 0;
}

Writer of article mock this get method like below.

mockRepo.Setup(s => s.Get(It.IsAny<Func<User, bool>>()))
    .Returns((Func<User, bool> expr) => DataInitializer.GetAllUsers().Where(u => u.UserName == CorrectUserName)
            .FirstOrDefault(u => u.Password == CorrectPassword));

mockRepo.Setup(s => s.Get(It.IsAny<Func<User, bool>>()))
    .Returns((Func<User, bool> expr) => DataInitializer.GetAllUsers().Where(u => u.UserName == WrongUserName)
            .FirstOrDefault(u => u.Password == WrongPassword));

Everytime I make a call Get method in my tests it goes second mock setup. And it doesn't consider parameters as username and password.

[Test]
public void AuthenticateTest()
{

    var returnId = _userServices.Authenticate(CorrectUserName, CorrectPassword);
    var firstOrDefault = _users.Where(u => u.UserName == CorrectUserName).FirstOrDefault(u => u.Password == CorrectPassword);
    if (firstOrDefault != null)
        Assert.That(returnId, Is.EqualTo(firstOrDefault.UserId));
}

How can i mock and test this Authenticate method.

Both your setups refer to the same method call:

mockRepo.Setup(s => s.Get(It.IsAny<Func<User, bool>>()))

This will register a mock method for every call of Get that passes any Func<User, bool> . This is regardless of the actual value of the function that is passed.

Since both setups refer to the same call, the second setup will overwrite the first, so you effectively only have the later one.

If you want to return different things depending on the value that is passed as an argument, you have two choices: Either you change the setup to expect a more explicit value (other than a IsAny<T> ), or you change the return function to actually check the value. For example:

mock.Setup(x => x.DoSomething(2)).Returns(4);
mock.Setup(x => x.DoSomething(3)).Returns(9);

Or:

mock.Setup(x => x.DoSomething(It.IsAny<int>()).Returns(v => {
    if (v == 2)
        return 4;
    else
        return 9;
});

In your case, you want to change the return value depending on a lambda expression you pass. This is actually a bit more complicated. The easiest is probably to provide a static list of users and run the actual filter on that:

mockRepo.Setup(s => s.Get(It.IsAny<Func<User, bool>>())).Returns(expr => {
    User[] users = new User[] {
        new User { UserName = CorrectUserName, Password = CorrectPassword },
        new User { UserName = WrongUserName, Password = "foobar" },
    };
    return users.Where(expr).FirstOrDefault();
});

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM