问题

**版主注:**这里已经发布了39条答案(其中一些已被删除)。在你发布之前,请考虑是否可以在讨论中添加有意义的内容。你很可能只是重复别人已经说过的话。
我偶尔会发现自己需要在公共类中创建一个私有方法,只是为它编写一些单元测试。

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

其他人发现自己这样做,因为我真的不喜欢这样做吗?我个人认为奖金超过了公开方法的问题,并没有真正提供课外的任何服务......
更新
感谢大家的回答,似乎激起了人们的兴趣。我认为普遍的共识是测试应该通过公共API进行,因为这是一个类将被使用的唯一方式,我同意这一点。我上面提到的几个我在上面提到的案例是不常见的案例,我认为这样做的好处是值得的。

但是,我可以看到每个人都指出它永远不应该发生。当我考虑更多时,我认为更改代码以适应测试是一个坏主意 - 毕竟我认为测试是一种支持工具,并且如果你愿意,将系统更改为"支持支持工具",则是公然的不好的做法。


#1 热门回答(181 赞)

注意:这个答案最初是针对问题发布的。单独测试单独测试是否是通过getter公开私有实例变量的一个很好的理由?它被合并到这个中,所以它可能对那里提供的用例有点特别。

作为一般性陈述,我通常都会重构"生产"代码,以便更容易测试。但是,我认为这不是一个好的电话。一个好的单元测试(通常)不应该关心类的实现细节,只关心它的可见行为。你可以测试该类在调用first()last()之后按照你期望的顺序返回页面,而不是将内部堆栈暴露给测试。

例如,考虑这个伪代码:

public class NavigationTest {
    private Navigation nav;

    @Before
    public void setUp() {
        // Set up nav so the order is page1->page2->page3 and
        // we've moved back to page2
        nav = ...;
    }

    @Test
    public void testFirst() {
        nav.first();

        assertEquals("page1", nav.getPage());

        nav.next();
        assertEquals("page2", nav.getPage());

        nav.next();
        assertEquals("page3", nav.getPage());
    }

    @Test
    public void testLast() {
        nav.last();

        assertEquals("page3", nav.getPage());

        nav.previous();
        assertEquals("page2", nav.getPage());

        nav.previous();
        assertEquals("page1", nav.getPage());
    }
}

#2 热门回答(144 赞)

就个人而言,我宁愿使用公共API进行单元测试,而且我绝对不会将私有方法设置为public,以便于测试。

如果你真的想要单独测试私有方法,在Java中你可以使用Easymock/Powermock来执行此操作。

你必须务实,你也应该知道难以测试的原因。

'Listen to the tests' - 如果难以测试,那能告诉你一些你的设计吗?你是否可以通过公共API进行重构,以便对这种方法的测试进行重构并轻松覆盖?

以下是Michael Feathers在'Working Effectively With Legacy Code中所说的内容

"很多人花了很多时间试图弄清楚如何解决这个问题...真正的答案是,如果你有测试私有方法的冲动,那么方法不应该是私有的;如果制作方法公众困扰你,很有可能,因为它是一个单独的责任的一部分;它应该在另一个阶级。" [由M. Feathers有效处理遗产守则(2005年)]


#3 热门回答(61 赞)

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

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


原文链接