美文网首页TDD(测试驱动开发)
TDD私有方法测还是不测

TDD私有方法测还是不测

作者: 生活如同马拉松_yaguang | 来源:发表于2018-05-08 00:10 被阅读6次

    记录和分享一个经典kata, 题目是如何打印一个钻石型的字符。

    Given a letter print a diamond starting with 'A'
    with the supplied letter at the widest point.
     
    For example: print-diamond 'E' prints
    
        A
       B B
      C   C
     D     D
    E       E
     D     D
      C   C
       B B
        A
    

    按照TDD的套路,从最简单的开始,第一个开始 'A'。

         @Test
        public void Print_A_Test() {
            String[] expected = {"A"};
            String[] actual = DimondPrint.print('A');
             diamodAssert(expected, actual);
        }
    
        public static String[] print( char val) {
            return new String[] {"A"};
        }
    

    接着第二个用例如下:

        @Test
        public void Print_B_Test() {    
            String[] expected = {           
                 " A ",
                 "B B",
                 " A "
            };
            String[] actual = DimondPrint.print('B'); 
            diamodAssert(expected, actual);
        }
    
         public static String[] print( char val) {
        
            if( val ==  'B') {
            
                return new String[] {
                    " A ",
                    "B B",
                    " A " 
                 };
            }
            return new String[] {"A"};
        }
    

    第三个测试用例就是,测试字符'C'

       @Test
        public void Print_C_Test() {
        
            String[] expected = {           
               
                    "  A  ",
                    " B B ",
                    "C   C",
                    " B B ",
                    "  A  "
                    };
                    
            String[] actual = DimondPrint.print('C'); 
            diamodAssert(expected, actual);
        }
    

    当时在做到这个时候,明显感觉步伐太大,需要小步分解。
    第一个思路就是可以将这个菱形分解成四个小正方形, 左上,右上,左下,右下,然后两两合并。这个就有一组方法.

    String[] getLeftTopSquare(char c){...}
    String[] getRightTopSquare(char c){...}
    String[] getRightDownSquare(char c){...}
    String[] getLeftDownSquare(char c){...} 
    String[] mergeOnVertical(String[] top, String[] down){...}
    String[] mergeOnHorizontal(String[] top, String[] down){...}
    

    但是面临的问题是:
    如何用TDD驱动这些方法?陷入两难选择。

    1. 如果将这组方法作为一个私有方法,但是没法很好的支持测试。
    • 如果当作公共接口去调用,问题是如果出错,定位不准去。
    • 测试的细节穿透力不强
      通过共有接口或者方法,去测试后面的一个私有方法. 私有方法逻辑简单问题不大, 如果这个私有方法比较复杂,就有隔靴搔痒。没有直接测试私有方法那么给力。
    直接测试 vs 间接测试
    1. 如果将这些方法,作为一个共有的方法。
    • 暴露出DiamondPrint 类的内部实现部分。
    • 第二使得不同层次的api暴露出来,抽象不统一,接口可读性差。
    • 另外这些方法测测试与基于需求的测试用例混合在一起,是的需求不清晰。因为这个不是同一个层面的抽象放在一起,可读性就会差很多。
        @Test
       public void Print_A_Test() {
           String[] expected = {"A"};
           String[] actual = DimondPrint.print('A');
            diamodAssert(expected, actual);
       }
    
      @Test
       public void Print_mergeOnVertical_Test() {
           String[] expected = {"A","B"};
           String[] actual = DimondPrint.mergeOnVertical("A", "B");
            diamodAssert(expected, actual);
       }
    
    
    1. 提取工具类。

    将上面一组方法提取到一个抽象类,比如DiamondUtility; 然后对这个类用TDD来开发。然后在上面的类里面调用已经测试好的方法。也就是从下往上开发。好处也很明显。

    • 直接测试,给力;
    • 测试反馈定位准确;
    • 测试代码可读性提升;
    • API的抽象层次统一。

    其实还有很有争议的两个方法。

    1. test recycle。 直接修改测试用例,使得代码和测试用例渐进演化,最终实现。自己感觉不推荐,中间的测试用例被删除了,使得测试用例分解过程,以及如果后期代码维护出错反馈定位不准确。看代码文章

    2. 与3 相反,测试自顶向下;对于没有实现的部分,先用hardcode,然后再逐步用代码替换一部分; 这个其实思路和1类似,是基于对外的接口来测试私有方法。缺点同上,中间测试用例没有,同样后期维护不方便。

    回到开始问题: 私有方法测试还是不测试?

    简单回答,NO! 私有方法要通过对外的接口来测试;如果私有方法复杂,提取出工具类去测试。

    Note: 这个题目有很多种解法,下一篇来介绍。

    相关文章

      网友评论

        本文标题:TDD私有方法测还是不测

        本文链接:https://www.haomeiwen.com/subject/yklmrftx.html