测试驱动开发。如何在创建方法之前为此转换编写单元测试?

时间:2017-04-15 23:06:08

标签: java tdd

我写了两种格式的迷你检查器: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;
    }

3 个答案:

答案 0 :(得分:2)

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

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

  

测试整个范围将永远

的确,如果你有数百甚至数千个场景,你就不应该测试所有场景,因为这些场景需要很长时间才会实现,除了单元测试可能会变成 <强>执行时间太长 这与单元测试原则相悖 单元测试必须快速执行,因为这些测试经常执行。

  

对几个随机myPosID值运行单元测试(蒙特卡罗   仿真)似乎更好,但即便通过测试并不意味着   得多。

使用随机值进行测试,建议不应使用随机序列 每次执行测试时生成的都不同,因为这些测试可能无法再现。
这也违背了单元测试原则 单元测试必须在任何环境和任何时间产生相同的结果 否则意味着测试不可靠。

因此,以TDD方式创建单元测试的想法是编写与要处理的案例类型一样多的单元测试。

例如:您有3种表示单元格的方法:

  

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

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

1)当我有Long值时,只有空单元格,我正在等待字节数组表示为......

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

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

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

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

最后,关于你是否应该测试所有案件的问题,我认为你应该把注意力集中在如上所示的“大案件”上。
应该没问题。

答案 1 :(得分:1)

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

  • 极端情况:空输入,例如
  • 一些一般情况
  • 测试性能的极大案例

所以你也可以遵循这个技术,但在规模上,这适合你。

另外,我应该提一下,“规范TDD”不适用于类似公式的方法,因为你总是可以通过另一个if进行测试。相反,我们应该关注事实,测试不仅为我们提供了正确的算法实现,还为我们提供了正确的设计。

答案 2 :(得分:1)

使用TDD检查公式很难。您可以使用Monte-Carlo的变体。生成1000(或100 000)个随机Long testID个数字。将它们保存在某处。请务必使用此列表检查前进和转发。 ID将是随机的,但不会在测试之间发生变化。这样你遵循“测试必须产生相同的结果”。

当公司拥有大量廉价但平庸的员工时,TDD似乎运作良好。然后管理员可以强制执行编写测试(很容易检查方法是否缺少测试),并且其他编码器更难以提交破坏现有代码的补丁(您的提交未通过JUnit测试 - 去重做!!)。测试会减慢工作人员的速度,但这并不重要,只要测试不会减慢经理的速度。只需雇用更多的程序员。

当项目从头开始时,这种方法特别有效。

如果编码员体面,他们的劳动力很昂贵而且项目已经成熟,那么你最好通过行为驱动测试。