如果我想用表达式作为参数进行单元测试,我应该做些什么呢?

本文关键字:单元测试 我应该 做些什么 参数 表达式 如果 | 更新日期: 2023-09-27 18:14:58

如何为这个方法编写单元测试:

public void ClassifyComments()
{
   IEnumerable<Comment> hamComments = _commentRepository.FindBy(x => x.IsSpam == false);
   IEnumerable<Comment> spamComments = _commentRepository.FindBy(x => x.IsSpam == true);
   //....
}

FindBy方法接受一个表达式作为参数:

public virtual IEnumerable<T> FindBy(Expression<Func<T, bool>> filter)
{
    return dbSet.Where(filter).ToList();
}

这是我到目前为止的单元测试:

IEnumerable<Comment> spamComments = Builder<Comment>.CreateListOfSize(10).All()
    .With(x => x.Content = "spam spam spam")
    .Build();
IEnumerable<Comment> hamComments = Builder<Comment>.CreateListOfSize(10).All()
    .With(x => x.Content = "ham ham ham")
    .Build();

var mockRepository = new Mock<IGenericRepository<Comment>>();
mockRepository
        .Setup(x => x.FindBy(It.Is<Expression<Func<Comment, bool>>>(y => y.IsSpam == true)))
        .Returns(spamComments);
mockRepository
        .Setup(x => x.FindBy(It.Is<Expression<Func<Comment, bool>>>(y => y.IsSpam == true)))
        .Returns(hamComments);

但是我不能编译它,我怎么能改变这个测试,使模拟生成hamCommentsspamComments的值。

错误2 'System.Linq.Expressions.Expression>'不包含'IsSpam'的定义和没有扩展方法'IsSpam'接受类型为的第一个参数"System.Linq.Expressions.Expression>"可以找到(您是否缺少using指令或程序集?参考?)

如果我想用表达式作为参数进行单元测试,我应该做些什么呢?

您试图在测试中重新创建要测试的逻辑,我认为这是您的错误。试试下面的代码:

IEnumerable<Comment> spamComments = Builder<Comment>.CreateListOfSize(10).All()
    .With(x => x.Content = "spam spam spam")
    .Build();
IEnumerable<Comment> hamComments = Builder<Comment>.CreateListOfSize(10).All()
    .With(x => x.Content = "ham ham ham")
    .Build();
// Bring both lists together as one (might be a better way to do this)
IEnumerable<Comment> allComments = spamComments.Union(hamComments);
var mockRepository = new Mock<IGenericRepository<Comment>>();
mockRepository
        .Setup(x => x.FindBy(It.Is<Expression<Func<Comment, bool>>>())
        .Returns(spamComments);

注意:我不是Moq专家,所以上面的语法可能不是100%正确的

因此,ClassifyComments方法中的代码的目的是获取混合Comment实例的列表,并将它们分成不同的分类(spam和ham),因此您只希望mockRepository返回单个注释列表。

然后由你的单元测试的其余部分来验证你的ClassifyComments在知道它有10个"垃圾邮件"和10个"火腿"Comment实例的情况下做了它应该做的事情。

作为一个例子,让我们假设你的ClassifyComments看起来像这样:

public void ClassifyComments(out IEnumerable<Comment> spam, out IEnumerable<Comment> ham)
{
   IEnumerable<Comment> hamComments = _commentRepository.FindBy(x => x.IsSpam == false);
   IEnumerable<Comment> spamComments = _commentRepository.FindBy(x => x.IsSpam == true);
   //....
   // Set the out params
   spam = spamComments;
   ham = hamComments;
}

你的单元测试应该是这样的:

public void TestClassifyComments() {
    IEnumerable<Comment> spamComments = Builder<Comment>.CreateListOfSize(10).All()
        .With(x => x.Content = "spam spam spam")
        .Build();
    IEnumerable<Comment> hamComments = Builder<Comment>.CreateListOfSize(10).All()
        .With(x => x.Content = "ham ham ham")
        .Build();
    // Bring both lists together as one (might be a better way to do this)
    IEnumerable<Comment> allComments = spamComments.Union(hamComments);
    var mockRepository = new Mock<IGenericRepository<Comment>>();
    mockRepository
            .Setup(x => x.FindBy(It.Is<Expression<Func<Comment, bool>>>())
            .Returns( (Expression<Func<Comment, bool>> predicate => spamComments.Where(predicate));
    // Construct the class which has the ClassifyComments method
    var sut = ...;
    IEnumerable<Comment> ham;
    IEnumerable<Comment> spam;
    sut.ClassifyComments(out spam, out ham);
    Assert.That(spam.Length, Is.EqualTo(10));
    Assert.That(ham.Length, Is.EqualTo(10));                
}

编辑

好的,我对Moq做了一些研究,我认为上面的代码是你如何用Moq做到这一点。