如何编写unit testing以validation函数是否对其结果进行排序?

我有一个数据源,我可以从中请求一个(任何)国家/地区的人员列表,以及一个从该数据源检索人员并按字母顺序按名称对其进行排序的方法。 我应该如何编写unit testing以确保我的方法的排序部分正常工作?

这就是我的SUT的样子:

class PeopleStuff { public IData data; public List getSortedPeopleForCountry(String countryName) { List people = data.getPeopleForCountry(countryName); Comparator nameComparator = new PersonNameComparator(); Collections.sort(people, nameComparator); return people; } } 

这就是我的unit testing的样子:

 @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 mockPeopleList = new ArrayList(); 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 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 getSortedPeopleForCountry(String countryName) { List people = data.getPeopleForCountry(countryName); Comparator nameComparator = new PersonNameComparator(); List sortedPeople = new ArrayList(people) Collections.sort(sortedPeople, nameComparator); return people; // oops! } 

我应该这样离开它添加使用真实比较器的模拟比较器,但也要validation它们是否被调用?

我做得对吗?

我认为您当前的测试非常好 – 测试是现实的,执行所有代码,并且您正在模拟数据源并使用dependency injection来提供模拟数据源。 在这个测试中有很多最佳实践。

关于你是否应该嘲笑比较器(因此在testGetPeopleSortsByPeopleName进行纯unit testing),你肯定会得到两个不同的意见:

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

我个人的意见是你应该保持原样,事实上,你有一个高质量,可读的测试,运行所有的代码,有效地断言你的要求远比担心严格纯粹的unit testing更重要。

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

 @Test public void testGetPeopleSortsByPeopleName() { peopleStuff.data = buildMockDataSource(COUNTRY, "A", "D", "B", "C") List result = peopleStuff.getSortedPeopleForCountry(COUNTRY); assertPersonList(result, "A", "B", "C", "D") } private IData buildMockDataSource(String country, String ... names) { ... } private void assertPersonList(List people, String ... names) { ... } 

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

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

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