【问题标题】:Unit test a method that sorts a collection单元测试对集合进行排序的方法
【发布时间】:2016-04-26 04:12:42
【问题描述】:

我有一个方法可以根据这样的属性对集合进行排序:

public List<Student> GetAllStudents()
{
    return _studentCatalogContext.Student.Where(x => (x.Course != 2 && x.Course != 6))
                                 .OrderByDescending(x => x.EnrollDateTime).ToList();
}

因此,在这种情况下,我们的想法是让最近注册的学生优先。

由于方法调用的结果将是一个带有最新注册的排序列表,所以我首先编写了一个这样的测试:

[TestMethod]
public void Calling_GetAllStudents_ReturnsSortedListOfStudents()
{
    var studentsList = new List<Student> {
    new Student {
                     Id = "123",
                     EnrollTime = "02/22/16 14:06:56 PM",
                     Course = 1
                 },
     new Student {
                     Id = "456",
                     EnrollTime = "03/30/16 12:50:38 PM",
                     Course = 3
                 }
                 };

    _studnentRepository.Setup(x=>x.GetAllStudents()).Returns(studentsList);

    Assert.AreEqual("02/22/16 14:06:56 PM", studentsList[0].EnrollTime);
}

有人建议此测试无效,因为它设置了一个值,然后对其进行断言。

在这种情况下我该如何编写正确的单元测试?

【问题讨论】:

标签: c# unit-testing mocking moq


【解决方案1】:

您按原样进行测试是一个空测试(它测试您构建的列表是否包含您刚刚放入的第一个元素)。

在目前的形式中,您的accepted answer 有同样的问题。它为您的存储库设置一个模拟,然后验证模拟是否返回您告诉模拟的信息。

为了有效地测试存储库逻辑,您需要模拟它的依赖关系,在本例中为_studentCatalogContext。由于您没有在问题中提供此信息,因此我将假设以下课程:

public class Student {
    public string Id {get;set;}           // Weird this is a string not int
    public string  EnrollTime {get;set;}  // Weird this is a string not date
    public int Course  {get;set;}
}

public class StudentCatalogContext : DbContext
{
    public virtual IDbSet<Student> Student { get; set; }
}

public class StudentRepository
{
    private StudentCatalogContext _studentCatalogContext;
    public StudentRepository(StudentCatalogContext context)
    {
        _studentCatalogContext = context;
    }
    public List<Student> GetAllStudents()
    {
        return _studentCatalogContext.Student.Where(x => (x.Course != 2 && x.Course != 6))
                                     .OrderByDescending(x => x.EnrollTime).ToList();
    }
}

注意,我将目录上下文注入到存储库中。这是必需的,以便可以模拟它。另外,请注意,我已将您的 LINQ 查询中的 EnrolledDateTime 重命名为 EnrollTime,以便它与您的代码示例的其余部分匹配。

然后可以按如下方式测试此代码:

// Construct the data to be returned by the student set mock.
// Note, you don't want this to be in the order that you're expecting
// otherwise, how do you know if it's been sorted...
var studentData = new List<Student> {
    new Student {
                     Id = "123",
                     EnrollTime = "02/22/16 14:06:56 PM",
                     Course = 1
                 },
     new Student {
                     Id = "456",
                     EnrollTime = "03/30/16 12:50:38 PM",
                     Course = 3
                 }
}.AsQueryable();

// Setup a mock of the student set, which returns the canned data
// prepared above
var dbSetMock = new Mock<IDbSet<Student>>();
dbSetMock.Setup(m => m.Provider).Returns(studentData.Provider);
dbSetMock.Setup(m => m.Expression).Returns(studentData.Expression);
dbSetMock.Setup(m => m.ElementType).Returns(studentData.ElementType);
dbSetMock.Setup(m => m.GetEnumerator()).Returns(studentData.GetEnumerator());

// Create a mock of the catalog context that returns
// the mocked set prepared above
var contextMock = new Mock<StudentCatalogContext>();
contextMock.Setup(x=>x.Student).Returns(dbSetMock.Object);

// Create the system under test, injecting the mock context
var repo = new StudentRepository(contextMock.Object);

// Call the method that we're actually testing
var fetchedData = repo.GetAllStudents();

// Validate that the information returned is what we're expecting
Assert.AreEqual("02/22/16 14:06:56 PM", fetchedData[0].EnrollTime);

值得指出的是,上面的测试失败了。这是因为被测代码按 EnrollTime 降序排列,所以列表中的第一项是 EnrollTime 为“03/30/16 12:50:38 PM”的项

【讨论】:

    【解决方案2】:

    测试列表是否正确排序在很大程度上是无关紧要的,因为它是一种内置框架方法,(您会假设)已经被框架设计者(在本例中为 Microsoft)测试并证明是正确的。

    对此方法的更好测试是确保只返回不在课程 2 或 6 中的学生,因为这是您在 Where 方法中的自定义逻辑。

    所以,你的测试可能是这样的:

    [TestMethod]
    public void Calling_GetAllStudents_ReturnsSortedListOfStudents()
    {
        var studentsList = new List<Student> {
        new Student {
                         Id = "123",
                         EnrollTime = "02/22/16 14:06:56 PM",
                         Course = 1
                     },
         new Student {
                         Id = "456",
                         EnrollTime = "03/30/16 12:50:38 PM",
                         Course = 2
                     }
                     };
    
        // mock out student repository to return list    
    
        var studentsList = _studentRepository.GetAllStudents();
    
        Assert.AreEqual(1, studentsList.Count);
        Assert.AreEqual("123", studentsList[0].Id);
    }
    

    【讨论】:

    • 您会惊讶于我在 LINQ 查询中看到错误的次数 - 我强烈建议测试这些错误 - 在大多数 BLOBA 中,这些是项目中最复杂的代码!
    • @Carsten 我提倡测试 LINQ 查询的逻辑部分,即Where 方法。但是,由于OrderByDescending 不包含任何逻辑并且只是一种内置方法,因此我在测试它时看不到任何价值。此外,您可能会争辩说 Order By 语句仅适用于 UI,可以在 UI 测试而不是单元测试中涵盖。
    • 如果 OP 忘记了它或者有人不小心将它重构了,... - 这是你应该测试它是否重要的​​一部分
    • 因为您提出的解决方案现在您什么都不做...(您测试自己设置的列表 - 甚至没有 Where) - 测试似乎也失败了。 .. 我会在代码审查时拒绝这个
    • 你们都知道你们嘲笑你们应该测试的方法吗?
    猜你喜欢
    • 2011-02-24
    • 1970-01-01
    • 1970-01-01
    • 2012-01-03
    • 2018-09-21
    • 2022-12-15
    • 2017-08-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多