我们应该修改单元测试的函数签名吗?

Should we modify a function signature for unit testing?

本文关键字:函数 修改 单元测试 我们      更新时间:2023-10-16

>假设我们有一个函数add()如下所示:

void add(int a, int b) {
int sum=a+b;
cout<<sum;
sendSumToStorage(sum);
}

这个简单的函数添加到输入值,将总和打印到控制台,并将其发送到某个外部存储(例如文件)。 这就是我们理想地希望在应用程序中使用它的方式(这意味着,我们不希望它返回任何内容)。

出于单元测试的目的,如果我们修改函数签名以返回sum,它是否有效(从设计角度来看)? 然后我们可以进行如下测试:

bool checkAdd() {
int res=add(3, 4);
if(res==7) return true;
else return false;
}

更好的是,这(返回一个值)是我们对其进行单元测试的唯一方法吗? 是否有一些有效的方法可以在不更改函数签名的情况下对add()函数进行单元测试?

像你例子中的函数被认为是非常糟糕的做法。

为什么这么说呢?

好吧,你有一个名为add的方法,它添加两个数字调用其他东西。基本上你的方法不是做一件事,而是做两件事,这违反了单一责任原则。

这使得测试变得更加困难,因为您不能单独测试添加方法。

因此,您可以将其分成两个具有良好名称的方法,以反映它们的作用并分别测试它们。

如果您不希望方法之间的状态出现问题,则必须从有意义的位置开始返回结果。

忽略了这个例子有一个不好的意图。

对于这种情况,当您想要检查一些内部行为而不是 API 时,您应该尝试使用一些测试库,如 gtest 和 gmock。 它允许您描述更复杂的期望,而不仅仅是函数结果。

例如,您可以使用宏设置在代码执行期间将调用某个方法EXPECT_CALL期望值。

更多细节在这里: https://github.com/google/googletest/blob/master/googlemock/docs/ForDummies.md

回答您的问题时,出于测试目的修改测试代码的任何部分始终是一种不好的做法。在这种情况下,您不再测试生产代码。正如之前所建议的那样,最好将功能拆分为较小的部分并单独测试它们。

更改代码设计以提高可测试性是非常常见的,通常被认为是一种很好的做法。 显然,并非所有此类变化都一定是真正的改进 - 有时存在更好的解决方案。

在您的情况下,代码很难测试,因为它将计算(加法)与依赖组件(输出和存储数据)的交互相结合。 在你的情况下(正如Andrei指出的那样),该函数也违反了SRP,但是混合计算和交互通常会使测试更加困难,即使在没有违反SRP的情况下也是如此。

我了解,您将更改函数,以便除了打印并将其写入存储之外,它还将返回计算值。 这将允许您测试函数的计算部分。 但是,该函数将仅进行部分测试。 并且,该函数的目的将被进一步混淆。

相反,如果将函数拆分为一个执行计算的函数和一个执行交互的函数,则可以使用单元测试彻底测试第一个函数,并对另一个函数使用集成测试。 同样,这种方法的有用性与代码是否违反SRP无关。