使用全局变量的单元测试方法

6
假设我有以下类结构:
private string GlobalVariable = "foo";

public void MainMethod()
{
    string bar = Baz();
}

public string Baz()
{
    return GlobalVariable + "qux";
}

我希望对 Baz() 进行单元测试,使用不同的值来测试 GlobalVariable。但是,由于 GlobalVariable 只是在方法内部被调用,而不是作为参数传递给它,因此我无法在单元测试方法中设置它。
因此,我考虑改变我的结构为:
private string GlobalVariable = "foo";

public void MainMethod()
{
    string bar = Baz(GlobalVariable);
}

public string Baz(string globalVar)
{
    return globalVar + "qux";
}

现在我可以在单元测试中更改参数值globalVar以检查不同的输出。

但是,我的第一个结构更干净,因为我没有将变量值不必要地作为参数传递给方法。

有没有一种方法既能兼顾两者,又不必妨碍我的结构就能运行单元测试呢?


2
不,这就是为什么静态类很难测试的原因。正如您所发现的那样,全局状态无法进行有效的测试。 - Simon Whitehead
1
等一下.. 这是静态的吗?你的示例中什么定义了“全局”? - Simon Whitehead
1
@DavinTryon 没错。对于代码中的任何错误,我道歉了。我在 StackOverflow 上快速编写,没有时间进行单元测试;) - Curtis
@SimonWhitehead,您是在建议我只测试“MainMethod”方法吗?请记住,这只是我为了表达我对全局变量问题的看法而制作的一个简单示例。 - Curtis
1
@Curt - 复制太多了。以下链接应该有效 - http://misko.hevery.com/code-reviewers-guide/flaw-brittle-global-state-singletons/ - Lieven Keersmaekers
显示剩余9条评论
2个回答

6

我会考虑使用重载方法,这样就可以有一个带参数的和一个不带参数的...

public string Baz()
{
    return Baz(GlobalVariable);
}

public string Baz(string globalVar)
{
    return globalVar + "qux";
}

这里的好处是,您仍然拥有可以从代码中调用的没有参数的方法,而无需担心每次都要指定值,但是在测试和必要时使用不同值时,您会有重载的方法。
虽然您仍然无法测试第一个方法使用不同的值,但我认为仅测试第二个函数就足够了。

或者,如果您使用的是C# 4.0,您可以使用可选参数替代:

public string Baz(string globalVar = null)
{
    if(string.IsNullOrEmpty(globalVar))
        globalVar = GlobalVariable;
    return globalVar + "qux";
}

3
重载看起来在这里是个好主意,这样可以让我兼顾两者的优势。但是我必须确保这被很好地记录下来,否则,在类中看到没有明显用途的重载方法可能会让其他开发人员感到困惑!这种情况下是否是标准做法?似乎这在单元测试中是一个常见问题,但我找不到以前解决方案的参考。 - Curtis
1
对我来说,“标准做法”通常是“随心所欲”。如果是我以前从未遇到过的问题,我就会选择感觉可行的方法。如果在6个月后发现任何问题,那么这就是下次解决相同问题时可以尝试不同方法的教训。我曾经使用过这种方法,当我想要默认参数,但也允许偶尔覆盖时,我没有遇到任何问题,因此没有理由不再将其用于未来。 - musefan
我已经实现了这个解决方案。我改变了我的想法,认为将值作为参数传递使得方法更像一个“单元/组件”,不依赖于外部变量,从而避免了混淆。 - Curtis

2

如果全局变量是不可变的(例如应用程序设置),我不会使用重载。相反,我会在测试代码中访问应用程序设置。所以,我会保持这样:

public string Baz()
{
    return ApplicationSetting.GlobalVariable + "qux";
}

那么在测试中,我会像这样做:

[Test]
public void Test()
{
     string expected = ApplicationSetting.GlobalVariable + "qux";
     Assert.AreEqual(expected, Baz());
}

那么被测试的代码和测试本身都使用相同的应用程序设置源。这意味着源代码可以更改,但测试不会出现问题。希望这能帮到你。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接