【问题标题】:What should I change if I want unit test a method with expression as a parameter?如果我想对一个以表达式为参数的方法进行单元测试,我应该改变什么?
【发布时间】:2014-09-18 18:17:49
【问题描述】:

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

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 指令或程序集 参考?)

【问题讨论】:

  • 您的单元测试似乎没有多大意义,如果FindByIGenericRepository&lt;T&gt; 上的一个方法,那么您为什么要创建您要测试的系统的模拟实例?
  • 我不想测试 GenericRepository 类的 FindBy 方法。我需要在方法 ClassifyComments 中为第一个和第二个 FindBy 设置返回值。
  • 你用的是什么模拟框架?
  • 我使用 AutoMoq。如果我在方法 ClassifyComments 中只有一个 FindBy,我可以使用 AutoMoq 中的 It.IsAny。不幸的是,我有两个具有不同参数的 FindBy。

标签: c# unit-testing moq automoq


【解决方案1】:

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

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);

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

因此,ClassifyComments 方法中代码的目的是获取混合的 Comment 实例列表并将它们分成不同的分类(垃圾邮件和火腿),所以你只希望你的 mockRepository 返回单个 cmets 列表。

然后由单元测试的其余部分来验证您的 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 来做到这一点的。

【讨论】:

  • 不幸的是,这并不容易,因为 ClassifyComments 方法中的 list od hamComments 和 spamComments 必须不同,现在它们都具有相同的 20 个元素。在我没有复制的 ClassifyComments 方法的实现中,它非常重要。
  • @michael 好的,看看我的编辑是否能更好地解释我想要传达的想法。
猜你喜欢
  • 2017-07-23
  • 1970-01-01
  • 2023-03-05
  • 1970-01-01
  • 2011-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多