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")
在设置我的存储库时,我正在尝试 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")