【问题标题】:How do I write a unit test to verify that a function sorts its result? [duplicate]如何编写单元测试来验证函数是否对其结果进行排序? [复制]
【发布时间】:2013-07-15 06:31:28
【问题描述】:

我有一个数据源,我可以从该数据源请求居住在(任何)国家/地区的人员列表,以及从该数据源检索人员并按姓名字母顺序对他们进行排序的方法。我应该如何编写单元测试以确保我的方法的排序部分正常工作?

这就是我的 SUT 的样子:

class PeopleStuff {

    public IData data;

    public List<Person> getSortedPeopleForCountry(String countryName) {
        List<Person> people = data.getPeopleForCountry(countryName);

        Comparator nameComparator = new PersonNameComparator();
        Collections.sort(people, nameComparator);

        return people;
    }

}

这就是我的单元测试的样子:

@Test public void testGetPeopleSortsByPeopleName() {
    String COUNTRY = "Whatistan";

    // set up test (the 3 lines below are actually in a @Before setup method)
    PeopleStuff peopleStuff = new PeopleStuff();
    IData mockData = createNiceMock(IData.class);
    peopleStuff.data = mockData;

    // set up data
    List<PersonName> mockPeopleList = new ArrayList<PersonName>();
    mockPeopleList.add(new Person(COUNTRY, "A"));
    mockPeopleList.add(new Person(COUNTRY, "D"));
    mockPeopleList.add(new Person(COUNTRY, "B"));
    mockPeopleList.add(new Person(COUNTRY, "C"));

    when(mockData.getPeopleForCountry(COUNTRY)).thenReturn(mockPeopleList);

    // exercise
    List<String> result = peopleStuff.getSortedPeopleForCountry(COUNTRY);

    // assert
    assertEquals("A", result.get(0).name);
    assertEquals("B", result.get(1).name);
    assertEquals("C", result.get(2).name);
    assertEquals("D", result.get(3).name);
}

我需要知道的是,我存根数据、运行测试和做出断言的方式是否正确,或者是否有更好的方法。

我的应用程序有很多测试方法和很多自定义排序算法;我实现了所有测试以使用我这样存根的一些 4 个值,in a "random" order,我在编写测试时选择它。


我是否应该只测试比较器是否被调用?这对我来说似乎不对,因为我不知道他们是否被要求提供正确的数据或在 getSortedPeopleForCountry() 内部的算法中的正确时间。我想检测这样的情况:

public List<Person> getSortedPeopleForCountry(String countryName) {
    List<Person> people = data.getPeopleForCountry(countryName);

    Comparator nameComparator = new PersonNameComparator();
    List<Person> sortedPeople = new ArrayList<Person>(people)
    Collections.sort(sortedPeople, nameComparator);

    return people; // oops!
}

我是否应该这样添加模拟比较器,这些比较器使用真正的比较器,但也要验证它们是否被调用?

我做得对吗?

【问题讨论】:

  • 我会对列表进行排序,然后开始成对比较其元素,以确保 current 元素与上一个。
  • @LuiggiMendoza 我已经这样做了,但我改变了主意。这在测试中增加了对PersonNameComparator 的依赖。并且它需要一个 3 行的 for 循环,这意味着测试中有一个额外的算法,读者必须理解它才能理解测试。
  • 你让它看起来 3 行代码很难理解......事实上,因为我不使用 easymock,所以上面所有的代码对我来说都比一个更难理解带有if 的简单for 循环。
  • @LuiggiMendoza:然后证明@Test 方法作为问题的答案。
  • 参考this Q/A

标签: java unit-testing junit easymock


【解决方案1】:

我认为您当前的测试非常好 - 测试是真实的,运行所有代码,并且您正在模拟数据源并使用依赖注入来提供模拟数据源。此测试中有很多最佳实践。

关于是否应该考虑模拟比较器(因此将testGetPeopleSortsByPeopleName 上的测试作为纯单元测试)的问题,您肯定会在这里得到两种不同的意见:

  • 纯粹主义者会争辩说,您的测试在技术上是一个集成测试,并且要进行适当的单元测试,您需要调整您的测试以使用模拟比较器,然后单独测试比较器。
  • 实用主义者会争辩说您的测试已经是高质量的,而且它不是最严格意义上的单元测试并不重要。此外,将其拆分为两个单独的单元测试可能会降低测试的可读性 - 如果您涉及模拟比较器,我想上面的测试会是这种情况。

我个人的意见是,你应该保持原样,事实上,你拥有一个高质量、可读的测试来练习所有代码并有效地断言你的需求,这比担心有严格的纯单元测试要重要得多。

测试看起来需要改进的唯一方法是测试方法的长度 - 我认为一点方法提取可以帮助提高可读性并使测试方法更具表现力。我的目标是这样的:

@Test public void testGetPeopleSortsByPeopleName() {

    peopleStuff.data = buildMockDataSource(COUNTRY, "A", "D", "B", "C")

    List<String> result = peopleStuff.getSortedPeopleForCountry(COUNTRY);

    assertPersonList(result, "A", "B", "C", "D")
}

private IData buildMockDataSource(String country, String ... names) {
    ...
}

private void assertPersonList(List<Person> people, String ... names) {
    ...
}

【讨论】:

  • 好的,所以我采用了这个方法,最终得到了大量的存根生成方法和列表断言方法,但结果很好,因为我只是把它们都放在了一个测试帮助文件中。测试看起来非常整洁且易于维护。谢谢。
【解决方案2】:
ObjectA[] arr = objectAList.toArray(new ObjectA[objectAList.size()]);
for (int i = 0; i < objectAList.size() - 1; i++) {
        int j = i + 1;
        assertTrue(arr[i].getDate().compareTo(arr[j].getDate()) >= 0);
 } 

此代码表示包含 ObjectA 对象的 ArrayList 按字段日期降序排序的示例。我们正在检查列表中的成员与其前任的日期是否相同。

【讨论】:

    【解决方案3】:

    将排序逻辑与返回列表分开。所以我会让 getPeopleForCountry(String countryName) 只返回一个列表,而一个排序列表将从 getSortedPeopleForCountry(List ) 返回。这样你就可以在排序前后测试它是如何工作的。此外,您可能希望重写 Equals() 方法来比较名称(如果这是您想要使用的名称),但是您稍后会想要与其他一些属性进行比较。那是你的决定。

    【讨论】:

    • 这是非常模糊的,它完全忽略了我写的我有 Person 对象的自定义比较器。此外,这段代码只是我在开发中的一个简化版本。问题是关于如何测试一个函数看起来像我写的例子是对给定数据进行排序。
    • 它如何忽略你写的比较器?根据我的回答,您的比较器将在 getSortedPeopleForCountry(List T)... 中使用。无论如何,这是我对你的代码的看法。如果你有像 testGetPeopleForCountryWhenNotSorting() 这样的测试方法,你可以测试其他非排序方法不会测试。
    猜你喜欢
    • 1970-01-01
    • 2015-02-20
    • 2020-05-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-08-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多