9

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

这就是我的 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 个值,按照我在编写测试时选择的“随机”顺序。


我应该只测试是否调用了比较器吗?这对我来说似乎不对,因为我不知道他们是否被要求提供正确的数据或在内部算法中的正确时间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!
}

我应该这样保留它添加使用真实比较器但还要验证它们被调用的模拟比较器吗?

我做对了吗?

4

3 回答 3

3

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

关于是否应该模拟比较器(因此在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) {
    ...
}
于 2013-07-17T07:34:51.713 回答
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 按字段日期按降序排序。我们正在检查列表中的成员与其前任的日期是否相同。

于 2017-02-03T11:21:49.687 回答
0

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

于 2013-07-16T21:47:45.813 回答