单元测试:检查是否已调用特定的Comparable

时间:2017-04-10 15:49:51

标签: java unit-testing sorting collections mockito

考虑这种方法,我想为它编写一个单元测试。

public void update(List toUpdate) {
        //...
        Collections.sort(toUpdate, new SpecificComparator());
        //...
}

我想确保我们使用SpecificComparator实现来命令给定列表。无论如何要做到这一点?

我正在考虑使用工厂来检索SpecificComparator实现,这样我就可以与Mockito核实我们正在调用工厂,但是这不能确保我按照这个比较器订购列表,我们可以想象有人在方法中调用工厂但没有用它来排序列表...

或者,我还可以验证toUpdate列表对象的顺序,在我的单元测试中使用相同的比较器命令另一个列表并检查它们是否以相同的方式排序?

5 个答案:

答案 0 :(得分:3)

这么多回答,都包含一个"真相"但是错过了其他人悲伤。

因此:你应该绝对避免使用模拟来测试比较器的排序调用。相反,你测试两个方面:

  1. 您为比较器编写单元测试。哎呀,比较器有一种方法;具有非常明确的语义定义。你专注于编写单元测试来单独测试那个东西。因为这是A)易于操作和B)单元测试的整体想法:你尽可能在最小的单元上进行多次测试。
  2. 你写了几个单元测试,以确保你的管道和#34;是正确的。而且你不会在那里嘲笑任何东西。确保使用比较器对包含已知内容的列表进行排序;然后你assertThat(actualList, is(expectedList));
  3. 长话短说:比较者本身就是一个单位。你开始测试那件事;所有角落的情况和什么不是。然后确保应该对列表进行排序的方法确实带有排序列表。

    如果您的代码仍然需要考虑嘲笑,那么很有可能您的设计可以改进,易于测试(而且难以测试)。

答案 1 :(得分:1)

我们的想法是将列表排序为 IF 使用特定的比较器。这很重要,因为比较器的行为不应该改变,如果确实如此,那么应该写出新的要求,这意味着用于测试的列表必须改变。但是,在大多数情况下,您不应经常更改单个比较器的行为。因此,您应该创建已经排序的列表,就像使用比较器一样,然后您可以调用以下方法

public boolean unitTestComparator(List sorted, Comparator comp)
{
    List shuffled = new List(sorted);
    Collections.shuffle(shuffled);
    return sorted.equals(Collections.sort(shuffled, comp));
}

现在,您可以对各种列表使用多个测试来为不同的比较器运用所有边缘情况。所有这一切的关键在于你知道在使用比较器之后列表应该是什么样子,唯一的繁琐部分是找到每个比较器的所有边缘情况。您还可以运行for循环并使用此方法根据需要多次测试它,因为您为方法提供了正确的列表格式。它所做的只是将随机化随机化。

请注意,这是随机测试,您还可以在方法中添加另一个参数,该参数可以是您想要的方式洗牌,以查找特定的边缘情况。

答案 2 :(得分:1)

我想说这不是一件合理的事情。

单元测试应该只在其公共接口上测试一个类,并且由于比较器是隐藏的实现细节,因此单元测试的业务不是如何实现排序顺序。

这意味着如果实施某天改变,要通过其他方式实现相同的排序顺序,测试将继续通过 - 这就是事情应该如何。

因此,您应该测试输出是否按照您期望的顺序排序,但是您不应声明有关该方法专用的对象的任何内容。

当然,如果您创建了该类API的Comparator部分,那将是另一回事:

public class Updater {

    private final Comparator sortComparator;

    public Updater(Comparator sortComparator) {
       this.sortComparator = sortComparator;
    }

    public void update(List toUpdate) {

       //...

       Collections.sort(toUpdate, sortComparator);

       //...
     }

 }

...然后将模拟Comparator传递给构造函数并声明它已被使用是合适的 - 因为现在它对调用者很感兴趣,Comparator它是传入的是正在使用的那个,因此应该进行测试。

答案 3 :(得分:0)

你在上面提到过我称之为"交叉测试",但我认为这是不必要的,如果你真的想实现它,我会把它给你,让你更多地考虑它。测试使用不同的比较器更新列表两次,并断言结果是您所期望的。

public class CrossingTest {

    @Test
    public void sortWithSpecificComparator() throws Throwable {
        List<Integer> list = asList(0, 1, 2);
        List<Integer> reversedList = asList(2, 1, 0);
        Comparator<Integer> originalOrder = (a, b) -> a < b ? -1 : 1;

        assertThat(update(list, with(originalOrder)), equalTo(list));

        assertThat(update(list, with(originalOrder.reversed()))
                 , equalTo(reversedList));                    
    }

    private List<Integer> update(List<Integer> input, Comparator comparator) {
        List<Integer> list = new ArrayList<>(input);
        SUT it = new SUT(comparator);

        it.update(list);
        return list;
    }

    private Comparator with(Comparator comparator) { return comparator; } 
}

class SUT {
    private Comparator comparator;    

    public SUT(Comparator comparator) {
        this.comparator = comparator;
    }

    public void update(List toUpdate) {
        Collections.sort(toUpdate, comparator);
    }
}

答案 4 :(得分:-1)

虽然您可以使用以下方法模拟新对象:

SpecificComparator comparator = Mockito.mock(SpecificComparator.class);
Mockito.whenNew(SpecificComparator.class).thenReturn(comparator);

最好的方法是在调用方法后检查元素的排序,而不是检查何时/如何使用比较器。您可以使用Listequals方法执行此操作,这就是它所说的:

  

将指定对象与此列表进行比较以获得相等性。返回   当且仅当指定的对象也是列表时才为true,两个列表都是如此   具有相同的大小,以及两者中所有相应的元素对   列表是平等的。