关于java:如何重构方法以使其更容易测试

How to refactor a method to make it easier to test

下面是一个方法,我很难弄清楚如何使用JUnit进行测试。
这种方法很难测试,因为它取决于其他方法的结果(例如getClosestDcoumentCode)。

基于我对JUnit的阅读,这表明我应该重构该方法。 但是怎么样? 如果不需要重构,您如何测试依赖于其他方法的方法?

谢谢,

埃利奥特

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
private static String findPrincipal(List<DocumentKey> documentkeys_) {
    Hashtable<String, Integer> codecounts = new Hashtable<String, Integer>();
    for (DocumentKey document : documentkeys_) {
        int x = 0;
        String closestCode = getClosestDocumentCode(document.candidates);
        if (closestCode == null) continue;
        int thecount = 0;
        if (codecounts.containsKey(closestCode))
            thecount = codecounts.get(closestCode);
        if (document.hasKey)
            thecount += 2;
        else
            thecount++;
        codecounts.put(closestCode, new Integer(thecount));
        x++;

    }
    String closestCode = getClosestCode(codecounts);
    return closestCode;
}


好吧,首先,我想知道该方法是否真的需要是静态的,以及该类正在做什么。看起来它可能是一个GOD类,或者至少它违反了单一责任原则。 getClosestCode有什么作用?如果它是一个类,你可以在测试中将它与一个存根注入到测试类中。

EasyMock会让你模拟方法响应,但我不确定你如何模拟静态方法。

一般来说,你可能需要

  • 将长函数提取到类中
  • 使功能非静态
  • 维护单一责任主体

  • 听起来像getClosestCodegetClosestDocumentCode属于一组不同于findPrincipal方法的责任。所以你要首先将它们分成两个不同的类。为每个要创建的类创建一个接口。然后,实现findPrincipal方法的类可以依赖于另一个接口作为构造函数参数,如下所示:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    public class PrincipalFinderImpl implements PrincipalFinder
    {
        private CodeFinder codeFinder;
        public PrincipalFinderImpl(CodeFinder codeFinder) {
            this.codeFinder = codeFinder;
        }
        public String findPrincipal(List<DocumentKey> documentkeys_) {
            Hashtable<String, Integer> codecounts = new Hashtable<String, Integer>();
            for (DocumentKey document : documentkeys_) {
                int x = 0;
                String closestCode = codeFinder.getClosestDocumentCode(document.candidates);
                if (closestCode == null) continue;
                int thecount = 0;
                if (codecounts.containsKey(closestCode))
                    thecount = codecounts.get(closestCode);
                if (document.hasKey)
                    thecount += 2;
                else
                    thecount++;
                codecounts.put(closestCode, new Integer(thecount));
                x++;

            }
            String closestCode = codeFinder.getClosestCode(codecounts);
            return closestCode;
        }
    }

    现在,应该很容易创建另一个实现CodeFinder接口的类,手动或使用Mocking框架。然后,您可以控制每次调用getClosestCodegetClosestDocumentCode的结果,并确保使用您希望调用的参数调用这些方法中的每一个。


    我没有深入阅读这个方法。但是如果私有方法需要测试,则表明您的设计存在问题。至少Kent Beck这么认为。


    关于JUnit Second Edition的存根调用有一章,如果您认为现有代码没有写入测试驱动开发标准,我建议您查看一下。