主持人注:这里已经有39个答案了(有些已经删除了)。在你发表你的答案之前,考虑一下你是否可以为讨论添加一些有意义的东西。你很可能只是在重复别人已经说过的话。


我偶尔发现自己需要将类中的私有方法设为public,只是为了为它编写一些单元测试。

通常这是因为该方法包含类中其他方法之间共享的逻辑,并且单独测试逻辑更整洁,或者另一个原因可能是我想测试同步线程中使用的逻辑,而不必担心线程问题。

其他人发现他们这样做是因为我不喜欢吗?我个人认为,公开一个方法的好处超过了它在类之外没有提供任何服务的问题……

更新

谢谢大家的回答,似乎引起了大家的兴趣。我认为普遍的共识是测试应该通过公共API进行,因为这是使用类的唯一方式,我非常同意这一点。在我上面提到的几个案例中,我会这样做,这是不常见的情况,我认为这样做的好处是值得的。

然而,我可以看到,每个人都指出它不应该真的发生。再仔细想想,我觉得改变你的代码来适应测试是一个坏主意——毕竟我认为测试在某种程度上是一个支持工具,而改变一个系统来“支持一个支持工具”是明显的坏做法。


当前回答

正如其他人的评论所广泛指出的那样,单元测试应该关注公共API。然而,撇开利弊和论证不考虑,您可以通过使用反射在单元测试中调用私有方法。当然,您需要确保JRE安全性允许这样做。调用私有方法是Spring框架使用它的ReflectionUtils(参见makeAccessible(Method)方法)。

下面是一个带有私有实例方法的小示例类。

public class A {
    private void doSomething() {
        System.out.println("Doing something private.");
    }
}

以及执行私有实例方法的示例类。

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class B {
    public static final void main(final String[] args) {
        try {
            Method doSomething = A.class.getDeclaredMethod("doSomething");
            A o = new A();
            //o.doSomething(); // Compile-time error!
            doSomething.setAccessible(true); // If this is not done, you get an IllegalAccessException!
            doSomething.invoke(o);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (SecurityException e) {
            e.printStackTrace();
        }
    }
}

执行B,将打印正在进行私有操作。如果确实需要,可以在单元测试中使用反射来访问私有实例方法。

其他回答

在. net中,有一个叫做PrivateObject的特殊类,专门用于允许您访问类的私有方法。

在MSDN或Stack Overflow上查看更多信息

(我很奇怪,到目前为止还没有人提到它。)

但在某些情况下,这是不够的,在这种情况下,你必须使用反射。

尽管如此,我还是会坚持不测试私有方法的一般建议,然而,像往常一样,总会有例外。

正如其他人所说,单元测试私有方法有点可疑;单元测试公共接口,而不是私有实现细节。

也就是说,当我想对c#中私有的东西进行单元测试时,我使用的技术是将可访问性保护从私有降级为内部,然后使用InternalsVisibleTo将单元测试程序集标记为友程序集。然后,单元测试程序集将被允许将内部组件视为公共的,但您不必担心会意外地增加公共表面区域。

不,因为有更好的方法来剥猫皮。

一些单元测试利用依赖于类定义中的宏,当在测试模式中构建时,这些宏会自动扩展以创建钩子。很有C风格,但是很好用。

一个更简单的OO习惯用法是使您想测试的任何东西都是“受保护的”,而不是“私有的”。测试工具继承自被测试类,然后可以访问所有受保护的成员。

或者你选择“朋友”。就我个人而言,这是我最不喜欢的c++特性,因为它打破了封装规则,但它恰好是c++实现某些特性所必需的。

无论如何,如果您正在进行单元测试,那么您很可能需要向这些成员中注入值。白盒短信是完全有效的。这真的会破坏您的封装。

为什么不把堆栈管理算法分解成一个实用程序类呢?实用程序类可以管理堆栈并提供公共访问器。它的单元测试可以集中在实现细节上。对算法复杂的类进行深度测试非常有助于消除边缘情况并确保覆盖范围。

然后,当前类可以干净地委托给实用程序类,而不暴露任何实现细节。它的测试将与其他人推荐的分页需求相关。

就单元测试而言,你绝对不应该添加更多的方法;我相信你最好做一个关于你的第一个()方法的测试用例,它将在每次测试之前被调用;然后,您可以多次调用- next(), previous()和last(),以查看结果是否符合您的期望。 我猜如果你不给你的类添加更多的方法(只是为了测试的目的),你会坚持测试的“黑盒”原则;