Return 列表中的单个对象

Return a single object from list

在设置我的存储库时,我正在尝试 return 单个帐户对象。它应该 return 这个对象来自一个列表,其中用户名等于参数。

但是我遇到了异常

Object of type 'System.String' cannot be converted to type 'IncrediStaff.DataAccess.Models.Account'.

    [TestInitialize]
    public void Setup()
    {
        var repository = new Mock<IAccountRepository>();
        var accounts = new List<Account>
        {
            new Account { AccountId = 1, Username = "John", Hash = "9f3Iv0NW9Jr3l+EmOS/zWCPe96k=", Salt = "y7qwIY0ep8aHiiSwl57dt4ueuCo=" }, // Password is, "TestPassword"
            new Account { AccountId = 2, Username = "Ryan", Hash = "63mnR/gbFIIU6vGEFqoY5H1QCCI=", Salt = "xi/lkLFqPPTR5Q9rX3m/PJ4FH0rECyalYdyRJ6pCpfE=" }, // Password is, "NewPassword"
            new Account { AccountId = 3, Username = "Sarah", Hash = null, Salt = null, FirstLogin = 1 }
        };

        repository.Setup(x => x.GetAccount(It.IsAny<string>()))
            .Returns<Account>(r => accounts.FirstOrDefault(x => x.Username == It.IsAny<string>()));

        _service = new AccountService(repository.Object);
    }

    [TestMethod]
    [TestCategory("Accounts")]
    public void UserPasswordIsCorrect()
    {
        Assert.IsTrue(_service.Login("John", "TestPassword"));
    }

如果我 return 单个帐户对象,则测试通过。

        repository.Setup(x => x.GetAccount(It.IsAny<string>()))
            .Returns(new Account
            {
                AccountId = 1,
                Username = "John",
                Hash = "9f3Iv0NW9Jr3l+EmOS/zWCPe96k=",
                Salt = "y7qwIY0ep8aHiiSwl57dt4ueuCo="
            });

我不确定为什么它不能 return 列表中的单个对象。此外,我还有另一个使用相同列表且测试通过的设置。我不确定为什么这个有效而另一个无效。

        repository.Setup(r => r.SelectIfUsernameExists(It.IsAny<string>()))
            .Returns<string>(username => accounts.Exists(r => r.Username == username));

您应该检查此案例的来源以了解您的问题: https://github.com/moq/moq4/blob/b2cf2d303ea6644fda2eaf10bad43c88c05b395f/Source/Language/IReturns.cs https://github.com/moq/moq4/blob/b2cf2d303ea6644fda2eaf10bad43c88c05b395f/Source/MethodCallReturn.cs

具体来说,这个:

IReturnsResult<TMock> Returns<T>(Func<T, TResult> valueFunction);

文档说,T 是:

The type of the argument of the invoked method

所以 Returns 的泛型参数中 T 的类型实际上是第一个参数的类型,它在之前的设置方法中被传递到您正在模拟的函数中。

您的设置 x.GetAccount(It.IsAny<string>()) - 它模拟具有签名 Account GetAccount(string name) 的方法,因此调用的方法接受一个类型 string 的参数。 因此,当您执行 Returns 时,您的 IDE 已经知道 returned 参数的类型 - 它是文档中的 TResult。如果您不指定通用参数,它将始终 return 您在 .Returns() 中定义的对象。但是,如果您想根据 .Setup() 对其进行参数化,则需要明确说明在您模拟的函数中以何种顺序传递了哪些参数。 在您的情况下,您只传递了 1 个 string 类型的参数 - 它是 userName。因此,要进行参数绑定,您必须传递其类型 - string - 作为通用参数。

这样做也是为了让您可以为 .Returns 方法提供多个参数,例如: .Returns<string, int, DateTime>((string str, int i, DateTime date) => ...);

Returns 子句中的

It.IsAny<string>() 总是 return 为空。参见 this answer。所以你的

accounts.FirstOrDefault(x => x.Username == It.IsAny<string>())

真的是

accounts.FirstOrDefault(x => x.Username == null)

这将 return 为空。

相反,你需要像

这样的东西
accounts.FirstOrDefault(x => x.Username == "John")