繁体   English   中英

单元测试:检查是否已调用特定的可比对象

[英]Unit Test : check if a particular Comparable has been called

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

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

我想确保我们使用SpecificComparator实现对给定列表进行排序。 无论如何要这样做?

我当时正在考虑使用工厂来检索SpecificComparator实现,以便我可以通过Mockito验证我们正在调用工厂,但是这不能确保在给定该比较器的情况下列表是有序的,我们可以想象有人在调用该工厂。方法中的工厂,但不使用它来排序列表...

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

如此之多的答案,都包含一个“真相”,却缺少其他。 伤心。

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

  1. 您为比较器编写单元测试。 哎呀,比较器只有一种方法。 具有非常清晰的定义语义。 您专注于编写单元测试以单独测试该项目。 因为那是A)容易做到的,B)单元测试的整个思想:您在最小的单元上进行尽可能多的测试。
  2. 您编写一些单元测试以确保您的“管道”是正确的。 而且您不会在其中嘲笑任何东西。 您确保使用比较器对具有已知内容的列表进行排序; 然后您assertThat(actualList, is(expectedList));

长话短说:比较器本身就是一个单元。 您开始测试该东西; 与所有极端情况,什么不是。 然后,确保应该对列表进行排序的方法的确与排序后的列表一起返回。

而且,如果您的代码仍然需要考虑模拟,那么可能会将您的设计改进为易于测试(而不是难以测试)。

想法是, 如果使用了特定的比较器,则已对列表进行排序。 这很重要,因为比较器的行为不应更改,如果更改,则应写入新要求,这意味着必须更改用于测试的列表。 但是,在大多数情况下,您不应该过于频繁地更改单个比较器的行为。 因此,您应该创建已经使用比较器进行排序的列表,然后可以调用以下方法

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

现在,您可以对各种列表使用多种测试,以对不同比较器进行所有边缘测试。 所有这一切的关键在于,您知道使用比较器后的列表应该是什么样的,唯一麻烦的部分是找到每个比较器的所有边缘情况。 您还可以在该方法上运行一个for循环,以对其进行多次测试,因为您为该方法提供了正确的列表格式。 它所做的就是随机洗牌。

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

我会说这不是要做的合理的事情。

单元测试仅应在其公共接口上测试类,并且由于比较器是隐藏的实现细节,因此如何获得排序顺序与单元测试无关。

这意味着,如果某天实现发生更改,以通过其他方式达到相同的排序顺序,则测试将继续通过-事情就是这样。

因此,您应该测试输出是否按期望的顺序排序,但是您不应断言有关该方法专用的对象的任何信息。

当然,如果使Comparator成为类的API的一部分,那将是另一回事:

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是正在使用的,因此应该测试的东西。

您在上面提到的我将其称为“交叉测试”,但我认为这是不必要的,如果您真的想实现它,我会给您,让您多加考虑。 测试使用不同的比较器两次更新一个列表,并断言您期望得到结果。

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

尽管可以使用以下方法模拟新对象:

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

最好的方法始终是在调用方法之后检查元素的顺序,而不是检查何时/如何使用比较器。 您可以使用List的 equals方法来做到这一点,它就是这样说的:

比较指定对象与此列表是否相等。 当且仅当指定对象也是一个列表,并且两个列表具有相同的大小,并且两个列表中所有对应的元素对相等时,才返回true。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM