什么是测试在Java中使用另一个比较器的比较器的最佳方法?

时间:2018-04-27 03:39:54

标签: java unit-testing tdd comparator

从TDD的角度来看,我已经了解到,当某些行为中断时,只有一项测试失败 - 其他失败通常会产生误导。如果这是真的,我有两个比较器,其中一个比较器使用另一个比较器,你如何测试?我正在考虑将模拟用于"子比较器,"但是,如果使用"父比较器对列表进行排序,你会怎么注入这个模拟?"

例如:

public class SomeParentComparator implements Comparator<SomeType> {

    private static final SomeSubComparator subComparator1 = new SubComparator();
    private static final SomeOtherSubComparator subComparator2 = new SomeOtherSubComparator();

    @Override
    public int compare(SomeType someType1, SomeType someType2) {
        return new CompareToBuilder()
                .append(someType1.foo, someType2.foo, subComparator1)
                .append(someType1.bar, someType2.bar, subComparator2)
                .toComparison();
    }
}

在上文中,假设我已经测试过&#34;子比较器&#34; (SomeSubComparator和SomeOtherSubComparator)。在这种情况下如何在没有&#34;真依赖性的情况下测试SomeParentComparator&#34;在子比较器上(例如,模拟子比较器)?真的,这应该是一个&#34;工作流程&#34;单元测试,只需确保&#34;子比较器&#34;被叫,对吗?怎么样?

4 个答案:

答案 0 :(得分:1)

由于您直接在类对象的实例变量中初始化SomeParentComparatorsubComparator1,因此subComparator2很难独立测试。

我建议为这两个字段设置setter和getter,并使用setter或constructor初始化它们。

然后,您可以使用setters设置您的模拟subComaparators

您还可以创建模拟数据,其中subComparator比较并不重要。我可以给出一些类比,比如你想要用它们的名字和姓氏对People个对象进行排序。您的父级比较器按名字排序,子比较器按姓氏排序。然后,您的模拟数据将是People的列表,其姓氏都相同。

答案 1 :(得分:1)

理想情况下,所有类都会注入所有依赖项而不是隐式(在您的情况下,通过私有静态字段)。但是,删除所有隐式依赖项肯定会多次使代码过于复杂。在这种情况下,您有两种单元测试选项:

  1. 构建单元测试运行器,以便只有依赖类的测试通过才能运行依赖类的测试。

  2. 使用类似Powermock的东西在单元测试期间绕过封装并注入模拟的依赖项。这将允许依赖类的测试通过,即使依赖类已被破坏。

  3. 在您给出的示例中,我无法看到您无法明确依赖关系的任何原因。这些字段不需要是静态的 - 因为这个类的所有对象都将以完全相同的方式运行。因此,最好有一个明确的“子比较器”集合。并期望调用者明确添加它们。

答案 2 :(得分:0)

我认为你误解了TDD的建议。

您实际上有两个可以独立测试的子比较器类。然后你有了#34;父母&#34;比较器,将子比较器的实例作为硬连线组件。只需单独测试它们......没有任何花哨的嘲弄。

当然,父级的正确性取决于子比较器的正确性。但由于前者和后者是不可分割的,因此将父母视为黑匣子以进行测试更容易也更正确。

想到另一种方式,@ ShanuGupta的回答表明你应该打开父比较器的抽象来允许嘲笑。据推测,您有充分的理由封装了子比较器实例。现在你可以使用DI创建父构造函数了......但是你再一次有效地打破了这个抽象。

或者另一种方式。假设你打破了封装。现在你有了一个(概念上)应该为所有可能的子比较器类工作的父类。 (因为有人可以更改代码以使用不同的比较器而不更改父本身。)但这可能意味着您有一组更复杂的行为要进行测试。

答案 3 :(得分:0)

在这种情况下,我不会为嘲笑而烦恼,我会将整个事物(所有3个比较器)作为一个单元进行测试。

我想测试看起来像下面这样:

@Test
public void parent_with_smaller_foo_and_equal_bar_is_smaller() {
  var parentA = aParent().withFoo("A").withBar("C");
  var parentB = aParent().withFoo("B").withBar("C");

  assertThat(parentA).isLessThan(parentB);
}

@Test
public void parent_with_equal_foo_and_equal_bar_is_equal() {
  var parentA = aParent().withFoo("A").withBar("C");
  var parentB = aParent().withFoo("A").withBar("C");

  assertThat(parentA).isEqualByComparingTo(parentB);
}

等等。如果您将上述内容写成:

(A,C)<(B,C)
(A,C)=(A,C)

然后看起来我们需要至少3个案例来测试所有三个比较器:

(B,C)>(A,C)
(A,B)<(A,C)
(A,C)>(A,B)

我会将SubComparatorSomeOtherSubComparator保留为包私有类,并将其视为SomeParentComparator的实现详细信息。

只需5个测试用例,我就不会考虑找出失败原因成为真正的问题。