单身人士如何妨碍可测试性

时间:2015-12-09 11:07:22

标签: c# unit-testing tdd

我读到单身人士对可测试性不利。你能详细说明吗?不能嘲笑单身人士吗?他们写作是因为紧耦合,但我看不出原因。

1 个答案:

答案 0 :(得分:6)

这取决于你如何使用单例以及该单例是否实现了一个接口。如果它是单例的事实是实现细节,那么这可能没问题。如果你在要测试的代码中使用它的单例性质,那就更有问题了。

以我的Noda Time项目为例。它有一个IClock接口,代表“获取当前时间的方式”。这有一个名为SystemClock的实现,它是一个单例。现在考虑使用它的一些代码:

public class BadClass
{
    public bool IsIt2016Yet()
    {
        var now = SystemClock.Instance.Now;
        return now.InUtc().Year >= 2016;
    }
}

我们无法在不改变SystemClock.Instance的内容的情况下测试该代码。该代码与紧密耦合到单例类。但是,请考虑一下:

public class GoodClass
{
    private readonly IClock clock;

    public GoodClass(IClock clock)
    {
        this.clock = clock;
    }

    public bool IsIt2016Yet()
    {
        var now = clock.Now;
        return now.InUtc().Year >= 2016;
    }
}

现在你很好 - 在你的生产代码中设置了依赖注入,以便在SystemClock.Instance的任何地方使用IClock,但在测试代码中你可以使用假时钟 - 比如一个在NodaTime.Testing包中提供。在生产中,代码最终将使用单例,但它并没有与它紧密耦合。

当然,假设您已经适当地设置了依赖注入。如果你想在任何地方构建实例而不关心它需要一个时钟的事实,你可以选择可选将类耦合到SystemClock单例:

public GoodClass(IClock clock)
{
    this.clock = clock;
}

public GoodClass() : this(SystemClock.Instance)
{
}

现在它仍然可以测试(并且通常是灵活的),因为可以传递一个时钟,但它很方便,因为它有效地“默认”到系统时钟。 (您可以使用可选参数执行此操作,并使用null作为另一种选择,使SystemClock.Instance的参数结束。)