2017-04-15 97 views
2

我写了两种格式的代表主板的迷你检查器:long positionIDbyte[][] board。前者更便宜,后者更易于表现/操纵。转换本身很简单(请参阅下面的代码)。测试驱动开发。如何在创建方法之前为此转换编写单元测试?

TDD状态“写入一个失败的测试,然后写入生产代码”。如何进行表示转换?单元测试如
assertEquals(0L, toIndex(new byte[6][6]))不提供太多的报道。测试Long myPosID = 42L; assertEquals(myPosID, toIndex(toBoard(myPosID))不会增加太多价值。测试整个范围将永远持续下去。几个随机myPosID值(蒙特卡洛模拟)运行单元测试似乎更好,但即使通过测试并没有多大意义。

TDD应该怎么做?

/* 
    This class manipulates checkers board representation. Position is stored as long and represented as byte[height][width] board. 
    For board representation white = 0, black = 1, empty = 2. 

    Long positionID to byte[][] board: 
    get ternary numeral system representation of positionID, place positional values to corresponding squares. 

    For conversion from byte[][] board to long positionID: 
    long positionID = 0; for each (byte playableSquare : board){playable square positionID = positionID*3. positionID+= playableSquare} 
    */ 

    public final int boardHeight = 6; 
    public final int boardWidth = 6; 



    public long toIndex(byte[][] board) { 
     byte coords[] = new byte[boardHeight * boardWidth/2]; 
     int totalSquares = boardHeight * boardWidth/2; 
     byte k = 0; 
     for (int i = 0; i < boardHeight; i++) { 
      for (int j = 0; j < boardWidth/2; j++) { 
       byte makeItCheckers = (byte) ((i + 1) % 2); 
       coords[k] = board[i][j * 2 + makeItCheckers]; 
       k++; 
      } 
     } 
     long positionID = 0; 
     for (int i = totalSquares - 1; i >= 0; i--) { 
      positionID = positionID * 3 + coords[i]; 
     } 
     return positionID; 
    } 



    public byte[][] toBoard(long positionID) { 
     int totalSquares = boardHeight * boardWidth/2; 

     int[] coords = new int[totalSquares]; 

     for (int i = 0; i < totalSquares; i++) { 

      coords[i] = (int) (positionID % 3L); 
      positionID = positionID/3L; 
     } 
     byte[][] board = new byte[boardHeight][boardWidth]; 
     Arrays.fill(board, 2); 
     byte k = 0; 
     for (int i = 0; i < boardHeight; i++) { 
      for (int j = 0; j < boardWidth/2; j++) { 
       byte makeItCheckers = (byte) ((i + 1) % 2); 
       board[i][j * 2 + makeItCheckers] = (byte) coords[k]; 
       k++; 
      } 
     } 
     return board; 
    } 

回答

2

TDD在编写实现之前正在编写测试。
你似乎做相反的方式。

要为您的转换处理编写TDD和更一般的单元测试,您必须考虑验收测试。
您必须确定可能的转换处理方案。
你有什么作为输入和你期望的输出。


测试整个范围将采取永远

事实上,如果你有数百甚至数千的情况下,你不应该测试所有这些,因为这些都将成为长期实行并且除了单元测试可能变得太长而无法执行
这与单元测试原理相悖。
单元测试必须快速执行,因为它们经常执行。

几个随机myPosID值运行单元测试(蒙特卡洛模拟 )似乎更好,但即使如此,通过测试并不意味着 多。

测试与随机值你的建议不应使用在每个测试执行,因为这些可能无法重现的时间产生不同的随机序列 。
这也与单元测试原理相悖。
单元测试必须在任何环境和任何时间产生相同的结果。
否则表示测试不可靠。


因此,用于创建单元测试的想法是在TDD方式写尽可能多的单元测试类型的案件来处理。

例如:你有表示电池的3种方式:

白色= 0,黑= 1,空= 2

这些可以是3次验收测试的创建用于从Longbyte[][]的转换,反之亦然。

1)当我有作为Long值,只有空细胞,我在等待一个字节数组表达为...

2)当我有作为Long值,1个白细胞和空其余的单元格,我正在等待一个字节数组表示为...

3)当我有一个Long值,其余1个黑色单元格和空值,我正在等待一个字节数组表示为...

你可能会走得更远。
为什么不创建一个混合白色和黑色单元格的验收测试来检查混合它们不会产生副作用。

4)当我有作为Long值,3个白细胞,4个黑色细胞和空细胞的其余部分,我在等待一个字节数组表达为...

最后,关于您是否应该测试所有案例的问题,我认为您应该专注于上面所示的“大案例”。
应该没问题。

+0

是的,在TDD中,您首先编写一个失败的测试,然后编写通过测试的生产代码。我先写了我的代码。现在我想,TDD将会如何。他们在开始时不会有转换功能或测试。他们会写一个测试。测试会是什么样子? – Stepan

+0

当您执行TDD时,并不意味着您通过预先知道您将需要的所有处理。对于您的应用程序,业务需求将首先以TDD编码。正常:你从最初的业务需求开始,然后开始编写测试然后执行。一次,在开发过程中,出现了更好的需求或技术需求(这里将数据从格式转换为另一种格式),您必须完成最初的业务需求。所以你在TDD中指定它们。 – davidxxx

+0

让我们将这个逻辑应用于我的问题。我需要一个TEST,后来有一种方法从“Long positionID”的十进制表示到三进制数字系统,转换为“对于'positionID'的三进制数字表示中相应位置的每个方形赋值”。如何写这个测试?!。它或者不包括任何东西(检查0L,1L,2L并且说“我们通过!万岁!” - 我们假设一个测试并欺骗大学)或者它永远运行。方法本身是微不足道的。但是如果测试很难写,TDD的意义何在? – Stepan

1

在竞争性编程中存在类似的问题:当您提交代码时,系统无法验证代码的100%正确性,因为没有通过所有可能的输入。取而代之的是,系统运行许多测试,即分为三类:

  • 角落情况:空的输入,例如
  • 一些一般情况下
  • 非常大的情况下

所以测试性能比较你也可以遵循这个技术,但是在规模上,这适合你。

此外,我应该提到,“规范TDD”不适用于类似配方的方法,因为您总是可以通过另一个if通过测试。相反,我们应该关注事实,即测试不仅能够正确实现算法,还能够正确设计。

1

用TDD检查公式很难。您可以使用蒙特卡洛的变体。生成1000(或100 000)随机数Long testID。将它们保存在某处。始终使用此列表检查前后转换。 ID将是随机的,但不会因测试而改变。您按照这种方式“测试必须产生相同的结果”。

当公司有很多便宜但平庸的员工时,TDD似乎运作良好。然后管理员可以执行写作测试(很容易检查方法是否缺少测试),而其他编码人员难以提交打破现有代码的补丁(您的提交未通过JUnit测试 - 去和重做!!)。测试会减慢员工的速度,但这并不重要,只要测试不会减慢管理人员的速度。只需聘请更多的编码员。

这项工作特别好当项目从头开始。

如果编码员是体面的,他们的劳动力是昂贵的,一个项目成熟,那么你最好用行为驱动的测试。