Java 如何编写单元测试来验证函数是否对其结果进行排序?

Java 如何编写单元测试来验证函数是否对其结果进行排序?,java,unit-testing,junit,easymock,Java,Unit Testing,Junit,Easymock,我有一个数据源,我可以从中请求居住在(任何)国家的人的列表,还有一个方法,从该数据源检索这些人,并按他们的姓名字母顺序排序。我应该如何编写单元测试以确保方法的排序部分正常工作 这就是我的SUT的样子: class PeopleStuff { public IData data; public List<Person> getSortedPeopleForCountry(String countryName) { List<Person>

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

这就是我的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);
}
我应该像这样离开它,添加模拟比较器,它使用真实的比较器,但也要验证它们是否被调用


我做得对吗?

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

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

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

  • 纯粹主义者会争辩说,从技术上讲,您的测试是一个集成测试,要进行适当的单元测试,您需要调整您的测试以使用模拟比较器,然后单独测试比较器
  • 实用主义者会争辩说,你的测试已经是高质量的了,严格来说,它不是一个单元测试并不重要。此外,将其分为两个独立的单元测试可能会降低测试的可读性——我认为如果您使用模拟比较器,上述测试就是这种情况
我个人的观点是,您应该保持原样,事实上,您有一个高质量、可读性强的测试来执行所有代码并有效地断言您的需求,这比担心进行严格的纯单元测试要重要得多

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

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!
}
@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) {
    ...
}
@Test public void testGetPeopleSortsBypeleName(){
peopleStuff.data=buildMockDataSource(国家“A”、“D”、“B”、“C”)
列表结果=peopleStuff.getSortedPeopleForCountry(国家/地区);
资产个人列表(结果“A”、“B”、“C”、“D”)
}
私有IData buildMockDataSource(字符串国家/地区、字符串…名称){
...
}
私有void assertPersonList(列出人员、字符串…名称){
...
}
ObjectA[]arr=objectAList.toArray(新的ObjectA[objectAList.size());
for(int i=0;i=0);
} 

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

我会对列表进行排序,然后开始成对比较其元素,以确保当前元素的国家与前一个元素相同或更大。@LuiggiMendoza我已经这样做了,但我改变了主意。这在测试中增加了对
PersonNameComparator
的依赖。这需要一个3行的
循环,这意味着测试中有一个额外的算法,读者必须理解才能理解测试。你让它看起来像3行代码很难理解。。。事实上,因为我不跟easymock合作,对于我来说,上面所有的代码都比一个简单的
for
循环和
if
@LuiggiMendoza:然后证明一个
@Test
方法作为问题的答案要难理解得多。这是非常模糊的,它完全忽略了我编写的Person对象自定义比较器。而且,这段代码只是我在开发中所用代码的一个简化版本。问题是如何测试一个函数看起来是否像我写的示例那样对给定数据进行排序。它如何忽略您编写的比较器?您的比较器将在getSortedPeopleForCountry(列表T)内使用。。。从我的回答来看。不管怎样,这就是我对你代码的理解。如果你有一个测试方法,比如testGetPeopleForCountryWhenNotSorting(),你可以测试另一个非排序方法不会测试。好的,我就这样做了,最后得到了大量的存根生成方法和列表断言方法,但结果很好,因为我只是把它们都放在一个测试助手文件中。这些测试看起来非常整洁,易于维护。谢谢
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);
 }