Java 减少测试用例之间的耦合

Java 减少测试用例之间的耦合,java,unit-testing,testing,junit,tdd,Java,Unit Testing,Testing,Junit,Tdd,我试图学习更多关于JUnit和TDD的知识,但是我遇到了一些测试用例之间耦合的问题 当我为特定数据类型的API编写测试用例时,比如aDeque,我如何限制测试用例之间的耦合?例如,如果我正在为方法insertFirst(T item)编写一个测试用例,那么很容易假设在对正确初始化的对象调用该方法后,我应该能够断言两件事: Deque对象的大小应该增加1 如果我随后调用相应的T removeFirst()方法,它应该返回对我在初始调用中插入的对象的引用 然而,这在我的至少两个测试用例之间创建了一个

我试图学习更多关于JUnit和TDD的知识,但是我遇到了一些测试用例之间耦合的问题

当我为特定数据类型的API编写测试用例时,比如a
Deque
,我如何限制测试用例之间的耦合?例如,如果我正在为方法
insertFirst(T item)
编写一个测试用例,那么很容易假设在对正确初始化的对象调用该方法后,我应该能够断言两件事:

  • Deque
    对象的大小应该增加1
  • 如果我随后调用相应的
    T removeFirst()
    方法,它应该返回对我在初始调用中插入的对象的引用 然而,这在我的至少两个测试用例之间创建了一个不希望的耦合,其中一个测试用例的通过取决于另一个API方法的正确实现。例如,为了让这个测试用例通过,我需要一个正确的实现来检查
    Deque
    中的项目数,以及删除项目。如果出于任何原因,我对这两种方法的测试都不正确或不完整,那么我对
    insertFirst
    方法的测试将自动受到怀疑


    避免这种情况的最佳实践是什么?我编写测试用例的方法在某种程度上是错误的吗

    为一个方法编写测试时,必须假设类的其余部分工作正常。如果你不做这个假设,唯一的结论就是每堂课进行一次大规模的测试。我们不是这样做的

    您可以假设类的其他部分工作正常,因为也会对这些其他部分进行测试,以确保它们的正确性。
    如果一个部件工作不正常,则测试将失败,表明某些部件不正确。
    一旦测试套件的测试失败,就必须修复一个错误。你不能再做任何假设了

    例如:

    您有一个简单的列表实现,只有三种方法:

  • 插入
  • 除去
  • 计数
  • 您有三个测试:

  • 测试
    插入

    • 创建列表实例(排列)
    • 插入
      项目(Act)
    • 检查
      count
      是否等于1(断言)
  • 测试是否删除
    • 创建列表实例并插入项目(排列)
    • 删除
      项目(Act)
    • 检查
      count
      是否等于0(断言)
  • 测试
    计数
    • 创建列表实例并插入n项(排列)
    • 检索
      计数
      (Act)
    • 检查
      count
      是否等于n(断言)
  • 现在,如果上述任何一项测试失败,您就无法确定类中单个成员的正确性:

    • 如果第一次测试失败,第三次测试也会失败。第二个将通过,但实际上没有测试
      删除
      ,因为没有要删除的内容
    • 如果第二次测试失败,其他两次测试仍将通过。尽管如此,您仍然无法确定
      insert
      count
      是否正常工作,因为如果三个成员中的任何一个工作不正常,第二个测试将失败
    • 如果第三个测试失败,那么其他两个测试也很可能失败
    失败的测试告诉您一些事情:
    根据失败的测试,您通常可以扣除错误的位置。

    示例:如果只有第二个测试失败,而不是第一个或第三个测试失败,那么错误很可能出现在
    remove
    方法中。

    通常将单元测试视为测试特定功能而不是特定方法更有效。任何给定的测试都将检查一些方法集合是否能够正确地实现作为测试主题的功能,并且设计良好的测试集合中的失败模式将倾向于告诉您哪种方法很快就坏了

    一个好的测试集合往往会自然而然地脱离TDD;这是使这项技术如此强大的原因之一。如果我正在编写一个
    Deque
    ,我编写的测试通常如下所示,通常按以下顺序呈现

  • empty\u Deque\u isEmpty
    ——实现
    isEmpty
    始终返回
    true
  • non\u empty\u Deque\u isntEmpty
    ——实现
    insertFirst
    以使
    isEmpty
    实例变量为false
  • 重新清空\u Deque\u isEmpty
    ——将
    isEmpty
    使用的实例变量更改为响应
    insertFirst
    removeFirst
  • 是否为空\u Deque\u size\u正确
    ——实现
    size
    始终返回0
  • 是否为非空\u Deque\u size\u正确
    ——将实例变量添加到跟踪大小;意识到它正在做与isEmpty所需的相同的事情;重构
  • 清空了吗?大小正确吗?
    ——让测试通过,因为我们做了5。发生
  • 是否从\u empty\u Deque\u throw中删除\u
    -
    首先删除
    在执行其他操作之前需要检查
    大小
  • 是否已插入\u项\u返回
    --
    insertFirst
    removeFirst
    现在填充一个
    T
    实例变量
  • 是否插入\u项目\u从\u结束返回\u
    ——添加
    removeLast
    ,它是
    removeFirst
    的副本;重构
  • 是否插入了返回的项目
    ——添加复制
    insertFirst
    insertLast
    ;重构
  • 是否所有插入的项目都返回了
    ——更改
    插入优先
    移除优先
    ,以对
    某种类型的集合采取行动
    ;注意不要检查订单