单元测试代码,它调用静态方法

时间:2011-11-08 12:51:15

标签: .net unit-testing refactoring static-methods

我已阅读大多数与SO相关的问题(hereherethere)。 最后一个问题提出了四种替代方法来生成调用静态方法单元可测试的代码。    我想问一下我的具体情况:我们有一个“业务逻辑层”或“规则”项目,它包含45个静态类(没有状态,只有静态方法)。而且,它们不易自行测试:大多数都访问数据库和文件系统。无论如何,它并没有那么糟糕:要访问数据库,他们使用某些Mapper类的唯一实例(所有Mappers都是Singletons)。    每当我尝试对某些东西进行单元测试时,我都会碰到这堵墙。最大的问题是这是非常非常重要的代码,应该非常仔细地计划对其进行更改。    我的问题:我应该如何使这个单元更加可测试?我应该编写45个接口并使用依赖注入吗?即便如此,我如何存根/模拟Mappers?

PS:我一直在阅读Michael Feathers的“使用传统代码”,所以欢迎直接引用(其他书籍也是如此)

编辑:由于有些人说解决方案可能与平台有关,我正在研究.NET(C#和一些VB.NET)

5 个答案:

答案 0 :(得分:10)

目前的情况可能是没有人敢在代码中改变任何东西,因为它可能会以意想不到的方式破坏。确保每个人都明白您正在改善这种情况:您的更改可能会破坏代码,但不像之前,这些破坏将找到和< em>一旦找到它们,它们将被永久修复

尽管如此,下一步取决于您的经验以及您对团队的信任。如果你想安全玩,请使用这样的代码(Java语法):

Mapper {
    public static Mapper INSTANCE = new Mapper(); // NEW code

    protected void doImpl() { // NEW CODE
        ... code copied from impl()... // OLD code, NEW PLACE
    }

    public static void impl() { // OLD code
        INSTANCE.doImpl(); // NEW code
    }

    // OLD code ...
}

这是一个非常简单的更改,允许您从测试中覆盖INSTANCE。对于生产代码,您不做任何事情,默认情况下,代码的行为与以前完全一样。

这样,您可以一次替换一个方法。您可以随时停止关注此路径 - 每次更改只需几分钟即可进行重构:代码完全按照以前的方式执行。由于每次更改都很小并且无法破坏任何内容,因此您可以替换一种方法,编写之前无法写入的所有单元测试,冲洗,重复。最后,如果您不想/需要重新编写所有静态方法,这种方法可以为您提供所需的全部余地。

第二步,您可以介绍DI或任何其他让您开心的技术。这种方法的优点是:当您进行复杂的更改时,您将拥有可以保护您的单元测试。

如果你从DI开始,你必须在各种地方改变很多代码 - 没有适当的单元测试可以保护你。

答案 1 :(得分:3)

创建Mapper类的接口,并用新的moc实现替换mapper功能。我认为你需要实现抽象工厂,它将生成Mappers的实例。因此,创建一个IMapperFactory,这个DBMapperFactory和MocMapperFactory的两个实现将此对象的实例传递给您访问Mappers的代码,并使用此实例生成这些实例。

胃肠。

答案 2 :(得分:2)

您的问题与平台有关,在Microsoft世界中,您可以使用Microsoft Moles来测试静态方法。并模拟静态方法。

在java世界中,可能还有其他工具,或者你应该避免使用静态。

在其他平台上还有其他工具等。

通常,任何静态方法都会使您的代码不易测试。在我的选择中,最好不惜任何代价避免使用静态和单体(全局状态)。如果您稍后要更改任何内容,可测试代码是代码中最重要的部分。可测试代码通常也更具可读性。

Moles and Pex

答案 3 :(得分:1)

静态类可以被模拟,虽然不知道你正在使用什么语言/环境,但是没有办法说出如何。

我的代码库最终完全像你说的那样;我们创建了几十个静态类的接口和默认实现(不要惊慌 - 代码生成)。默认的impls只是委托给单身人士。

调用类使用默认的impls切换到DI,但随后更容易测试。依赖于其他静态类或单例的静态类移动到使用默认的impls。

你像任何其他类一样模拟一个单例 - 如果它有一个getInstance(或等价的)你可以让它返回你想要的任何东西。或者您可以使用相同的路线并使用DI。

答案 4 :(得分:1)

很棒的问题! 您可以通过创建一个继承自单例Mapper类的新类来模拟Mapper类,然后使用它来拦截对数据库的所有调用。