Java 在做可能可行的最简单的事情时,使用TDD或BDD命名方法

Java 在做可能可行的最简单的事情时,使用TDD或BDD命名方法,java,junit,tdd,bdd,Java,Junit,Tdd,Bdd,例如,我必须创建可以返回向量长度的向量类 首先,我添加了测试: @Test public void shouldReturnLengthOfVector() { Vector3D vector = new Vector3D(4d, 2d, -4d); assertThat(vector.length(), is(6d)); } 编写测试时,创建类Vector3D并添加方法存根 public double length() { // TODO Auto-generate

例如,我必须创建可以返回向量长度的向量类

首先,我添加了测试:

@Test
public void shouldReturnLengthOfVector() {
    Vector3D vector = new Vector3D(4d, 2d, -4d);

    assertThat(vector.length(), is(6d));
}
编写测试时,创建类Vector3D并添加方法存根

public double length() {
    // TODO Auto-generated method stub
    return 0;
}
考试不及格。通过考试最简单的事情是什么?硬编码值:

public double length() {
    return 6d;
}
考试通过了。现在我添加了一些检查“cornercase”的方法:

当然,这不会过去。我更改我的实现:

public double length() {
    return Math.sqrt(i * i + j * j + k * k);
}
一切都是绿色的


当我遵循“可能工作的最简单的事情”原则时,如何为方法选择名称?在这个示例中,方法
SomeCornerCase应该返回LengthofVector
,这不是一个好名称。

没有单元测试;这个方法的含义是否清楚。你必须运用你的判断力


最简单的名字就是
l
;)在决定你可以使用什么时,你也应该考虑清楚。也就是说,你必须考虑开发人员以及编译器会让你逃脱的原因。

< P>你也可以重构你的测试代码。代码>someCornercaseShouldReturnLengthOfVector是一个非常有效的名称,而您正处于“可能工作的最简单的事情”阶段。重构名称,以反映在进一步测试之后代码实际测试的内容


您还可以对测试代码进行注释,使其意图更加明确。;)

我在命名方法时遵循以下模式:

WhatImTesting\u whatartheimputs\u ExpectedResult

仅仅因为这对你来说是一个简单明了的案例并不意味着对其他人来说也是如此。虽然我的方法命名可能会变得冗长,但通常很清楚他们在测试什么。为了使用您的示例,我将我的方法命名为
Length\u ValidConstructorImputs\u CorrectLength()
,通过读取方法名称,我知道我们正在使用有效输入(来自构造函数)测试Length方法,应该返回正确的长度

例如,假设我想测试以下构造函数:

public class House
{
    Door theDoor;
    public House(Door aDoor)
    {
        if(aDoor == null)
        {
            throw new IllegalArgumentException();
        }
        theDoor = aDoor;
    }
}
我可能有两种方法来测试这一点:

  • Constructor\u ValidDoor\u ObjectCreated()
    :这将检查在Constructor参数有效时是否创建了房屋对象
  • 构造函数\u NullDoor\u exception thrown()
    :这将检查在将null参数传递到构造函数时是否引发异常

    • 最简单的方法可能是向
      shouldReturnLengthOfVector
      添加另一个断言:

      @Test
      public void shouldReturnLengthOfVector() {
          Vector3D v1= new Vector3D(4d, 2d, -4d);
          assertThat(v1.length(), is(6d));
      
          Vector3D v2 = new Vector3D(1d, -2d, -2d);
          assertThat(v2.length(), is(3d));
      }
      
      我意识到有些人认为每个测试都应该有一个断言。然而,我认为,如果您给出几个“正常”操作的示例,并为真正的角落案例(例如,通过NaN)保留单独的测试案例,那么它作为行为文档更有用


      另一种方法是对测试命名更加具体。在这种情况下,您不会创建一个测试“应该返回向量的长度”。相反,您会创建一组方法,如“相同的值为零长度”、“长度为1的向量”(可能有很多断言,改变不同的参数)、“带负值的向量”等等。

      我认为在这里的例子中,我要么听从Anon的建议,在同一个测试中加入第二个断言,巧妙地避开命名问题,要么,如果对“每个测试一个断言”规则抱有宗教信仰的话,将下一个测试命名为
      应该返回长度另一个向量

      "... 唯一真正好的评论是你找到一种不写的方式的评论。”“罗伯。C.马丁。所以我想避免它们。添加更多功能后,此方法仍然有效,因此重构只会影响其名称。@janis:它的名称不正是您的问题所涉及的吗?是的。因此,我正在努力想出一些好名字@brainimus的答案是我的第一个答案,但如果输入很复杂,它就不能很好地工作。这很棘手,因为如果我有负值,方法名就会变得很难看。我同意。方法名称可能很长,而且有点冗长。不过,在测试中我对这一点没意见,因为我希望能够读取方法名称并准确知道测试的内容,如果失败,我已经非常清楚从何处开始查找。在这一步中,您硬编码一个值以返回
      length()
      ?稍后,您将在几个步骤中替换它。为什么仅仅为了通过而让考试通过?@mattb我遵循TDD的三条定律。测试中的期望仍然有效,并为我提供了一个起点-在它变为绿色后,它应该保持绿色,否则会出问题。我倾向于同意,固执地遵循“每个测试一个断言”规则可能会导致一些丑陋和笨拙的测试套件。启示!我有几个假设是错误的:1)我没有做最简单的事情(它将是Vector3D(0,0,0)),2)示例没有说明问题,因为没有真正的角落案例3)我的测试方法命名过于宽泛-它必须更具体地说明正在测试的行为。
      @Test
      public void shouldReturnLengthOfVector() {
          Vector3D v1= new Vector3D(4d, 2d, -4d);
          assertThat(v1.length(), is(6d));
      
          Vector3D v2 = new Vector3D(1d, -2d, -2d);
          assertThat(v2.length(), is(3d));
      }