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

什么';测试使用Java中另一个比较器的比较器的最佳方法是什么?,java,unit-testing,tdd,comparator,Java,Unit Testing,Tdd,Comparator,从TDD的角度来看,我已经了解到,当某个行为中断时,只有一个测试应该失败——其他的失败通常是误导性的。如果这是真的,我有两个比较器,其中一个比较器使用另一个比较器,你如何测试它?我曾考虑过使用mock作为“sub comparator”,但在使用“parent comparator”对列表进行排序时,如何注入这个mock呢 例如: public class SomeParentComparator implements Comparator<SomeType> { priv

从TDD的角度来看,我已经了解到,当某个行为中断时,只有一个测试应该失败——其他的失败通常是误导性的。如果这是真的,我有两个比较器,其中一个比较器使用另一个比较器,你如何测试它?我曾考虑过使用mock作为“sub comparator”,但在使用“parent comparator”对列表进行排序时,如何注入这个mock呢

例如:

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();
    }
}
公共类SomeParentComparator实现Comparator{
private static final SomeSubComparator subComparator1=新的SubComparator();
私有静态最终SomeOtherSubComparator subComparator2=新建SomeOtherSubComparator();
@凌驾
公共int比较(SomeType someType1,SomeType someType2){
返回新的CompareToBuilder()
.append(someType1.foo、someType2.foo、子比较1)
.append(someType1.bar、someType2.bar、子比较2)
.toComparison();
}
}

在上面,假设我已经测试了“子比较器”(SomeSubComparator和SomeOtherSubComparator)。在这种情况下,如何在不对子比较程序(例如,模拟子比较程序)具有“真正依赖性”的情况下测试SomeParentComparator?实际上,这应该是一个“工作流”单元测试,只需确保调用“子比较器”,对吗?如何测试?

您的
SomeParentComparator
很难独立测试,因为您直接在类对象的实例变量中初始化
子comparator1
子comparator2

我建议为这两个字段使用setter和getter,并使用setter或构造函数初始化它们

然后,您可以使用
设置器
设置模拟的
子组件参数


您还可以创建一个模拟数据,其中比较不重要。我可以打个比方,假设你想用名字和姓氏对
对象进行排序。父比较器按名字排序,子比较器按姓氏排序。然后,您的模拟数据将是一个姓氏相同的
人的列表。

您的
SomeParentComparator
很难独立测试,因为您直接在类对象的实例变量中初始化
subComparator1
subComparator2

我建议为这两个字段使用setter和getter,并使用setter或构造函数初始化它们

然后,您可以使用
设置器
设置模拟的
子组件参数


您还可以创建一个模拟数据,其中比较不重要。我可以打个比方,假设你想用名字和姓氏对
对象进行排序。父比较器按名字排序,子比较器按姓氏排序。然后您的模拟数据将是一个姓氏相同的
人的列表。

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

  • 构造单元测试运行程序,使依赖类的测试仅在依赖类的测试通过时运行

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


  • 在您给出的示例中,我看不出为什么不能显式地表示依赖关系。字段不需要是静态的,因为该类的所有对象的行为方式都完全相同。因此,最好有一个“子比较器”的显式集合,并期望调用者显式地添加它们

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

  • 构造单元测试运行程序,使依赖类的测试仅在依赖类的测试通过时运行

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


  • 在您给出的示例中,我看不出为什么不能显式地表示依赖关系。字段不需要是静态的,因为该类的所有对象的行为方式都完全相同。因此,最好有一个“子比较器”的显式集合,并期望调用者显式地添加它们

    我认为你误解了TDD的建议

    实际上,您有两个可以独立测试的subcomparator类。然后是“父”比较器,它将子比较器的实例作为硬连线组件。只需分别测试它们。。。没有任何花哨的嘲弄

    当然,父级的正确性取决于子比较程序的正确性。但是,由于前者和后者是不可分割的,因此为了测试的目的,将父对象视为一个黑盒子是更容易和更正确的

    从另一个角度考虑,@ShanuGupta的回答建议您应该打破父比较器的抽象,允许模拟。大概您封装subcomparator实例是有充分理由的。现在您可以使用DI创建父构造函数。。。日分
    @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)
    
    (B,C)>(A,C)
    (A,B)<(A,C)
    (A,C)>(A,B)