C# 最小起订量';表达式<;Func<;T、 布尔>&燃气轮机;作为参数传入

C# 最小起订量';表达式<;Func<;T、 布尔>&燃气轮机;作为参数传入,c#,unit-testing,moq,C#,Unit Testing,Moq,我对单元测试和模拟非常陌生!我正在尝试编写一些单元测试,其中包括一些与数据存储交互的代码。数据访问由IRepository封装: interface IRepository<T> { .... IEnumerable<T> FindBy(Expression<Func<T, bool>> predicate); .... } 测试通过了,这向我表明Moq只响应与我在测试夹具中设置的代码完全相同的代码。显然,这在测试Sign

我对单元测试和模拟非常陌生!我正在尝试编写一些单元测试,其中包括一些与数据存储交互的代码。数据访问由IRepository封装:

interface IRepository<T> {
    ....
    IEnumerable<T> FindBy(Expression<Func<T, bool>> predicate);
    ....
}
测试通过了,这向我表明Moq只响应与我在测试夹具中设置的代码完全相同的代码。显然,这在测试SignupLogic.AddNewCompany是否拒绝任何重复的公司时并不特别有用

我已经尝试将moq.FindBy(…)设置为使用“Is.ItAny”,但这也不会导致测试通过


从我所读到的每一件事来看,我试图测试的表达式在这里并不能用Moq来做。可能吗?请帮忙

您通常只模拟您自己的类型。那些你不拥有的,真的不应该因为种种困难而被嘲笑。因此,正如你问题的名称所暗示的那样,嘲弄的表达方式并不是一条出路

在Moq框架中。为函数放置
.Returns()
非常重要,否则它将不匹配。因此,如果你没有这样做,那就是你的问题

repoMock.Setup(moq => moq.FindBy(c => c.Name == "Company Inc").Returns(....

你应该能够使用
It.IsAny()
来完成你想要做的事情。使用
It.IsAny()
可以简单地调整设置的返回类型,以测试代码的每个分支

It.IsAny<Expression<Func<Company, bool>>>()
It.IsAny()
第一个测试,返回一个company,不管哪个谓词将导致异常抛出:

var repoMock = new Mock<IRepository<Company>>();
repoMock.Setup(moq => moq.FindBy(It.IsAny<Expression<Func<Company, bool>>>())).Returns(new List<Company>{new Company{Name = "Company Inc"}});
var signupLogic = new SignupLogic(repoMock.Object);
signupLogic.AddNewCompany(new Company {Name = "Company Inc"});
//Assert the exception was thrown.
var repoMock=new Mock();
repoMock.Setup(moq=>moq.FindBy(It.IsAny())).Returns(新列表{newcompany{Name=“Company Inc”}});
var signupLogic=新的signupLogic(repoMock.Object);
signupLogic.addnewcommpany(新公司{Name=“Company Inc”});
//断言引发了异常。
第二个测试,将返回类型设为空列表,该列表将导致调用add:

var repoMock = new Mock<IRepository<Company>>();
repoMock.Setup(moq => moq.FindBy(It.IsAny<Expression<Func<Company, bool>>>())).Returns(new List<Company>());
var signupLogic = new SignupLogic(repoMock.Object);
signupLogic.AddNewCompany(new Company {Name = "Company Inc"});
repoMock.Verify(r => r.Add(It.IsAny<Company>()), Times.Once());
var repoMock=new Mock();
repoMock.Setup(moq=>moq.FindBy(It.IsAny()).Returns(newlist());
var signupLogic=新的signupLogic(repoMock.Object);
signupLogic.addnewcommpany(新公司{Name=“Company Inc”});
repoMock.Verify(r=>r.Add(It.IsAny()),Times.Once());

只有结构(和文字值)完全相同的
表达式才会匹配,这可能是正确的。我建议您使用
Returns()
的重载,它允许您使用调用模拟的参数:

repoMock.Setup(moq => moq.FindBy(It.IsAny<Expression<Func<Company, bool>>>())
        .Returns((Expression<Func<Company, bool>> predicate) => ...);
repoMock.Setup(moq=>moq.FindBy(It.IsAny())
.Returns((表达式谓词)=>…);

中,您可以使用
谓词
返回匹配的公司(如果匹配的公司不是您期望的公司,甚至可能引发异常)。不太漂亮,但我认为它会起作用。

他在模仿他拥有的类型-
存储库
表达式
不是模仿;它只用于匹配模仿上的调用。问题的标题是
Moq'ing表达式参数
公平评论Aliostad,标题有点不清楚,尽管我认为是问题的第一部分明确了我的意思——这毕竟是一个相当详细的代码样本问题,即使我自己也这么说:)即使如此,这个问题真的很简单“Moq'ing expression我尝试了这种方式,但当我使用Verify:Result Message:Moq.MockException:mock上预期调用一次,但被调用了0次:u=>u.Users.Any(a=>a.Email===.user.Email)配置的设置:u=>u.Users.Any(a=>a.Email==.user.Email),次。Never@Aliostad你的答案很旧,但不可行。Moq返回表达式的UnsupportedException异常。表达式必须用“It.Is/It.IsAny”传递,这给了我这样做的方法,至少为UniqueCompanyAccepted和DuplicateCompanyRejected测试获得了一些绿灯:)这正是我想要的,但是根据您在上面的答案中的评论,“…”部分是否有可能进一步扩展???i、 e是否确实尝试匹配传递到表达式中的公司?@ibramumtaz:如果您有一个包含所有公司的列表
公司
,则
。返回((表达式谓词)=>公司。其中(谓词))应该可以工作。但是,如果您需要更高级的功能,那么最好编写一个实现
IRepository
的类,而不是使用Moq,因为将复杂的行为构建到mock中是很麻烦的。只有在将另一个问题标记为答案后,您才会看到这一点,但这也会很有帮助。谢谢将
It.IsAny
与不检查参数的
Return()
组合使用的缺点是,您无法验证参数是否正确。当然,有时您确实希望接受任何参数值,但在这种情况下,您可能需要验证
SignupLogic
是否正确构造其查询(使用公司名称,而不是其他属性)。It.IsAny将始终通过。我更喜欢检查lambda表达式等式
m.FindBy(item=>item.CompanyID==1)
。实现这一点的一种方法是使用LambdaCompare:。通过使用这个比较类,新的设置将是
expressiontestexpression=item=>item.CompanyID==1;repoMock.Setup(m=>m.FindById(It.Is(criteria=>LambdaCompare.Eq(criteria,testExpression))).Returns(yourcomanylist)
如果您的目标是只测试返回,您可以强制您的模拟框架忽略参数:Mock.Arrange(()=>repo.AllIncluding(x=>x.Category==Category)).IgnoreArguments().Returns((新列表){new Product()}).AsQueryable());在上面的示例中,我使用的是JustMock的免费版本。
var repoMock = new Mock<IRepository<Company>>();
repoMock.Setup(moq => moq.FindBy(It.IsAny<Expression<Func<Company, bool>>>())).Returns(new List<Company>{new Company{Name = "Company Inc"}});
var signupLogic = new SignupLogic(repoMock.Object);
signupLogic.AddNewCompany(new Company {Name = "Company Inc"});
//Assert the exception was thrown.
var repoMock = new Mock<IRepository<Company>>();
repoMock.Setup(moq => moq.FindBy(It.IsAny<Expression<Func<Company, bool>>>())).Returns(new List<Company>());
var signupLogic = new SignupLogic(repoMock.Object);
signupLogic.AddNewCompany(new Company {Name = "Company Inc"});
repoMock.Verify(r => r.Add(It.IsAny<Company>()), Times.Once());
repoMock.Setup(moq => moq.FindBy(It.IsAny<Expression<Func<Company, bool>>>())
        .Returns((Expression<Func<Company, bool>> predicate) => ...);