如何在java中测试本地内部类方法?

时间:2010-07-28 13:30:47

标签: java unit-testing junit inner-classes

在许多应用程序中,我经常使用专用子算法(或简单定义好的代码片段)的算法。

直到现在,当我编写主算法时,我为每个子算法创建了一个私有方法,如下例所示(OldStyle):

public class OldStyle {

    public int mainAlg() {
        int x = subAlg01();
        int y = subAlg02();
        int z = x * y;
        return z;
    }

    private int subAlg01() {
        return 3;
    }

    private int subAlg02() {
        return 5;
    }
}

这很好但我不喜欢增加方法(subAlg01和subAlg02),即使是私有的,也只使用一种方法(mainAlg)。

最近我发现了使用本地内部类,现在我的例子是(NewStyle):

public class NewStyle {

    public int mainAlg() {
        class Nested {

            public int subAlg01() {
                return 3;
            }

            public int subAlg02() {
                return 5;
            }
        }
        Nested n = new Nested();
        int x = n.subAlg01();
        int y = n.subAlg02();
        int z = x * y;
        return z;
    }
}

我非常喜欢它,但现在我遇到了以下问题:如何使用JUnit测试subAlg01和subAlg02?

顺便说一下:我正在使用eclipse。

谢谢你的帮助。

编辑:我试着更好地解释一下:我说,我有一个排序算法,我想测试它以确保它按预期运行。这种排序算法只用于类X的方法m。我可以使它成为类X的私有方法,但是类X通常与排序无关,那么为什么要用排序方法“破坏”类X呢?所以我把它放在方法m里面。一段时间后,我想改进我的排序算法(我让它更快),但我想确保它的行为符合预期,所以我想用原始测试重新测试它。

这就是我想要做的,也许没有解决方案,我希望有人可以帮助我。

在回答选择后编辑。我选择Rodney的答案是因为他的解决方案是我采用的解决方案:一个独立的助手类帮助我(这是一个帮手!)清楚地了解子方法是什么,它也让我有能力测试它们。

7 个答案:

答案 0 :(得分:18)

您应该只测试类的公共接口,而不是私有成员或私有内部类。私有成员应该是实现细节,仅由类的公共方法(直接或间接)使用。因此,您可以通过其调用方法间接地对这些进行单元测试。如果您觉得在这些单元测试中没有足够的粒度,或者您无法感知(某些)您感兴趣的结果,这可能会对您的设计造成问题:课程可能太大,尝试要做太多,因此可能需要将其某些功能提取到一个单独的类中,然后可以直接对其进行单元测试。

在当前示例中,如果内部类本身包含大量代码,您可以简单地将其转换为顶级类,然后可以直接对其方法进行单元测试。

(如果它不需要引用封闭的类实例,那么你的内部类应该是static。)

答案 1 :(得分:11)

菲利普,我理解你对这个问题的沮丧和一些答案。当我多年前第一次开始使用JUnit时,我也想测试私有代码,我认为大师们说这是个坏主意是愚蠢的。

原来他们是对的(惊喜!),但只有在写完了一些测试之后才明白为什么。您可能需要经历相同的过程,但最终会得出相同的结论; - )

无论如何,在你的情况下,我会将Nested变成一个合适的独立类,可能在一个单独的包中,以明显它是一个帮助类。然后我会直接为它编写测试,独立于任何其他测试。

然后我会为NewStyle编写测试,并只关注NewStyle的行为。

(很可能我也会将Nested注入NewStyle而不是在NewStyle中实例化它 - 即使它成为NewStyle构造函数的参数。

然后,当我在测试中编写NewStyle的测试时,我将传递Nested的实例并继续。如果我觉得特别棘手,我会在Nested之外创建一个界面并创建第二个实现,并用它来测试NewStyle。)

答案 2 :(得分:1)

您无法从外部访问这些类,因此junit无法测试它们。你必须把事情公开来测试他们。

答案 3 :(得分:1)

您应该避免因为

而使代码过于复杂
  

不喜欢有扩散   方法

在您的情况下,您可以测试方法,如果它们是外部类的方法,或者如果您绝对需要内部类,则将其放在mainAlg()方法之外,以便它可以全局显示。

public class NewStyle {

    public int mainAlg() {

        Nested n = new Nested();
        int x = n.subAlg01();
        int y = n.subAlg02();

        int z = x * y;
        return z;
    }

    class Nested {

        public int subAlg01() {
            return 3;

        }

        public int subAlg02() {
            return 5;
        }
    }
}

然后可以使用new NewStyle().new Nested().subAlg01();

调用此方法

答案 4 :(得分:1)

嗯,我知道像Groovy这样的语言通常用于对Java进行单元测试,并且能够透明地访问私有字段和方法.....但我不确定嵌套类。我可以理解为什么你可能想要做这类事情,但我采取与测试getter和setter相同的位置,它们应该作为测试你的公共方法的副作用进行测试,如果它们没有以这种方式进行测试,那么他们为什么要开始呢?

答案 5 :(得分:1)

当你的内部类的行为恰好是方法所做的核心部分时,问题出现了:假设你有一个方法应该将runnable传递给第三个对象,这恰好是一个内部类,真的不应该不是一个单独的实体:在这种情况下,适当的单元测试几乎需要通过模拟第三个对象来运行所述内部类,并使用参数捕获来测试它做它应该做的事情。

这种问题在支持函数式编程的语言中非常常见,其中测试lambda传递给第三方几乎是一项要求。

但是,正如之前所说的那样,如果你的内部类别从未被外界使用过,那么它只是一个实现细节,单独测试就没用了。

答案 6 :(得分:1)

实际上,您可以测试您当地的内部课程。但它需要实现一个接口。这样,您可以通过反射创建本地类的实例并将其强制转换为其接口。获得接口类型后,您可以毫无问题地测试实现代码:

界面:

public interface Algorithm {
    int subAlg01();
    int subAlg02();
}

班级:

public class NewStyle {
    public int mainAlg() {
        class Nested implements Algorithm {

            public int subAlg01() {
                return 3;
            }

            public int subAlg02() {
                return 5;
            }
        }
        Nested n = new Nested();
        int x = n.subAlg01();
        int y = n.subAlg02();
        int z = x * y;
        return z;
    }
}

测试:

public class NewStyleTest {

    @Test
    public void testLocal() throws ClassNotFoundException,
            NoSuchMethodException, SecurityException, InstantiationException,
            IllegalAccessException, IllegalArgumentException,
            InvocationTargetException {
        Class<?> forName = Class.forName("NewStyle$1Nested");
        Constructor<?> declaredConstructor = forName
                .getDeclaredConstructor(NewStyle.class);
        declaredConstructor.setAccessible(true);
        Algorithm algorithm = (Algorithm) declaredConstructor
                .newInstance(new NewStyle());

        assertEquals(algorithm.subAlg01(), 3);
        assertEquals(algorithm.subAlg02(), 5);
    }
}