如何设置根据参数值动态 returns 结果的模拟?

How do I setup a mock that dynamically returns results based on a parameter's value?

我正在尝试测试 API 控制器方法是否仅 returns 用户(加上一些与此处无关的其他状态)匹配基于 Name where Name = FirstName + ' ' + LastName

我有一个公开了 GetUsersByName(name) 方法的存储库。我想为该存储库方法模拟一个设置,该方法将 return 一个 List<UserModel> 包含与某些存根用户列表 (this.testUsers) 中的名称标准匹配的用户。

我试过以下方法:

this.mockUserReposiotry.Setup(r => r.GetAllUsersByName(It.IsAny<string>()))
    .Returns(this.mockUtilities.MockUpUserModel(this.testUsers).Where(u => string.Concat(
        u.firstName, " ", u.lastName)
        .ToLower().Contains() );

但我不知道如何将 Contains 子句与我告诉 moq 匹配的 IsAny<string> 联系起来。

这可能吗?我怀疑我需要为 IsAny 提供一个参数,但我找不到任何类似的例子。

是的,这是可能的。您可以使用 Returns<string> 其中

Specifies a function that will calculate the value to return from the method, retrieving the arguments for the invocation

this.mockUserReposiotry.Setup(r => r.GetAllUsersByName(It.IsAny<string>()))
    .Returns<string>(originalParameter => this.mockUtilities.MockUpUserModel(this.testUsers).Where(u => string.Concat(
        u.firstName, " ", u.lastName)
        .ToLower().Contains(originalParameter) );

长答案

我能够构建一个示例测试来重现您所追求的。

[TestClass]
public class DynamicResultsTests {
    List<UserModel> testUsers = new List<UserModel>();
    string[] names = new[] { "John Doe", "Jane Doe", "Jack Sprat", "John Smith", "Mary Jane" };

    [TestInitialize]
    public void Init() {
        testUsers = names.Select(n => {
            var tokens = n.Split(new[] { " " }, StringSplitOptions.RemoveEmptyEntries);

            return new UserModel { firstName = tokens[0], lastName = tokens[1] };
        }).ToList();
    }

    [TestMethod]
    public void Test_ShouldDynamicallyReturnResultsBasedOnParameterValue() {
        //Arrange
        string parameterValue = "john";

        Func<string, UserModel, bool> predicate = (s, u) => string
                .Join(" ", u.firstName, u.lastName)
                .IndexOf(s, StringComparison.InvariantCultureIgnoreCase) > -1;

        Func<string, List<UserModel>> valueFunction = s =>
            this.testUsers.Where(u => predicate(s, u)).ToList();

        var mockUserRepository = new Mock<IUserRepository>();
        mockUserRepository
            .Setup(r => r.GetAllUsersByName(It.IsAny<string>()))
            .Returns<string>(valueFunction);

        var repository = mockUserRepository.Object;

        //Act
        var users = repository.GetAllUsersByName(parameterValue);

        //Assert (There should be 2 results that match john)
        users.Should().NotBeNull();
        users.Should().NotBeEmpty();
        users.Count().Should().Be(2);
    }

    public interface IUserRepository {
        List<UserModel> GetAllUsersByName(string name);
    }

    public class UserModel {
        public string firstName { get; set; }
        public string lastName { get; set; }
    }
}