4

我已经开始研究整个单元测试/测试驱动的开发理念,我越想它似乎就越能扮演与静态类型检查类似的角色。这两种技术都为程序中的某些类型的错误提供了编译时、快速响应的检查。但是,如果我错了,请纠正我,但似乎具有完全覆盖的单元测试套件将测试静态类型检查将测试的所有内容,然后是一些。或者换一种说法,静态类型检查只是“证明”您的程序正确的部分方式,而单元测试将让您尽可能多地“证明”(在一定程度上)。

那么,如果您也使用单元测试,是否有任何理由使用具有静态类型检查的语言?此处提出了一个类似的问题,但我想更详细地介绍。静态类型检查相对于单元测试有哪些具体优势(如果有的话)?我想到了编译器优化和智能感知等一些问题,但是对于这些问题还有其他解决方案吗?还有其他我没有想到的优点/缺点吗?

4

10 回答 10

11

关于软件质量,有一个不变的事实。

如果无法编译,则无法发布

在这条规则中,静态类型语言将胜过动态类型语言。

好的,是的,这条规则不是一成不变的。Web 应用程序可以在不编译的情况下发布(我已经部署了许多未编译的测试 Web 应用程序)。但基本正确的是

越早发现错误,修复成本就越低

静态类型语言将防止在软件开发周期的最早可能时刻发生真正的错误。动态语言不会。单元测试,如果你彻底达到超人的水平,可以代替静态类型语言。

然而何必呢?有很多非常聪明的人以编译器的形式为您编写一个完整的错误检查系统。如果您担心尽快出错,请使用静态类型语言。

请不要将这篇文章视为对动态语言的抨击。我每天都使用动态语言并喜欢它们。它们具有令人难以置信的表现力和灵活性,并允许令人难以置信的程序。然而,在早期错误报告的情况下,它们确实输给了静态类型的语言。

于 2009-01-06T04:30:50.207 回答
6

对于任何规模合理的项目,您不能仅通过单元测试来考虑所有情况。

所以我的回答是“不”,即使你设法解释了所有情况,你也因此首先破坏了使用动态语言的全部目的。

如果你想编写类型安全的程序,最好使用类型安全的语言。

于 2009-01-06T04:27:48.560 回答
5

我认为自动化单元测试对动态类型语言很重要,但这并不意味着它将在您应用的上下文中取代静态类型检查。事实上,一些使用动态类型的人可能实际上正在使用它,因为他们想要常量类型安全检查的麻烦。

动态类型语言相对于静态类型语言的优势远不止于测试,类型安全只是一方面。动态和静态类型语言的编程风格和设计差异也有很大差异。

此外,单元测试写得太用力强制类型安全意味着软件根本不应该是动态类型的,或者应用的设计应该用静态类型语言编写,而不是动态语言。

于 2009-01-06T04:29:34.063 回答
3

拥有 100% 的代码覆盖率并不意味着您已经完全测试了您的应用程序。考虑以下代码:

if (qty > 3)
{
    applyShippingDiscount();
}
else
{
    chargeFullAmountForShipping();
}

如果我输入 qty = 1 和 qty = 4 的值,我可以获得 100% 的代码覆盖率。

现在想象一下我的业务条件是“......对于 3 件或更多物品的订单,我要对运费应用折扣......”。然后我需要编写在边界上工作的测试。所以我会设计数量为 2,3 和 4 的测试。我仍然有 100% 的覆盖率,但更重要的是我在我的逻辑中发现了一个错误。

这就是我只关注代码覆盖率的问题。我认为充其量最终会导致开发人员根据业务规则创建一些初始测试。然后为了提​​高覆盖率,他们在设计新的测试用例时会参考他们的代码。

于 2009-01-06T04:42:59.590 回答
3

清单类型(我想你的意思是)是规范的一种形式,单元测试要弱得多,因为它只提供示例。重要的区别在于规范声明了在任何情况下都必须保留的内容,而测试仅涵盖示例。你永远不能确定你的测试涵盖了所有的边界条件。

人们也倾向于忘记声明类型作为文档的价值。例如,如果 Java 方法返回 a List<String>,那么我立即知道我得到了什么,无需阅读文档、测试用例甚至方法代码本身。对于参数也是如此:如果声明了类型,那么我知道该方法期望什么。

声明局部变量类型的价值要低得多,因为在编写良好的代码中,变量存在的范围应该很小。但是,您仍然可以使用静态类型:而不是声明类型,而是让编译器推断它。Scala甚至C#等语言都允许您这样做。

一些测试风格更接近规范,例如QuickCheck或它的 Scala 变体ScalaCheck根据规范生成测试,试图猜测重要的边界。

于 2009-01-06T06:08:51.117 回答
2

我会换一种说法——如果你没有静态类型的语言,如果你打算用那个代码做任何“真正的”事情,你最好有非常彻底的单元测试。

也就是说,静态类型(或者更确切地说,显式类型)比单元测试有一些显着的好处,这让我通常更喜欢它。它创建了更易于理解的 API,并允许以动态类型语言更困难的方式快速查看应用程序的“骨架”(即每个模块或代码部分的入口点)。

总结一下:在我看来,考虑到可靠、彻底的单元测试,动态类型语言和静态类型语言之间的选择主要是一种品味。有些人更喜欢一个;其他人更喜欢对方。为工作使用正确的工具。但这并不意味着它们是相同的——静态类型的语言总是在某些方面具有优势,而动态类型的语言总是在某些不同的方面具有优势。单元测试在最大限度地减少动态类型语言的缺点方面大有帮助,但它们并没有完全消除它们。

于 2009-01-06T04:47:55.290 回答
2

不。

但这不是最重要的问题,最重要的问题是:做不到有关系吗?

考虑静态类型检查的目的:避免一类代码缺陷(bug)。然而,这必须在所有代码缺陷的更大领域的背景下进行权衡。最重要的不是狭隘的比较,而是对代码质量的深度和广度、编写正确代码的难易程度等进行比较。如果你能想出一种开发风格/流程,使你的团队能够产生更高的质量如果没有静态类型检查,代码效率更高,那么它是值得的。即使在测试中存在静态类型检查会发现的漏洞的情况下也是如此。

于 2009-01-06T05:30:41.507 回答
1

如果你非常彻底,我想它可以。但是为什么要打扰呢?如果语言已经在检查以确保静态类型是正确的,那么测试它们就没有意义(因为您免费获得它)。

此外,如果您在 IDE 中使用静态类型语言,IDE 甚至可以在编译测试之前为您提供错误和警告。我不确定是否有任何自动化单元测试应用程序可以做到这一点。

于 2009-01-06T04:23:00.330 回答
0

鉴于动态、后期绑定语言的所有好处,我想这是单元测试提供的价值之一。您仍然需要仔细和有意地编码,但这是任何类型的编码恕我直言的第一要求。能够编写清晰简单的测试有助于证明您的设计和实现的清晰性和简单性。它还为以后看到您的代码的人提供了有用的线索。但我不认为我会指望它来检测不匹配的类型。但在实践中,我并没有发现类型检查真的能捕捉到很多真正的错误。如果您首先拥有清晰简单的编码风格,那么我发现这不是在实际代码中发生的一种错误。

对于 javascript,我希望 jsLint 会发现几乎所有类型检查问题。主要是通过建议替代编码样式来减少您的曝光率。

于 2009-01-06T04:28:37.170 回答
0

类型检查有助于在系统中的组件之间强制执行合同。单元测试(顾名思义)验证组件的内部逻辑。

对于单个代码单元,我认为单元测试确实可以使静态类型检查变得不必要。但是在一个复杂的系统中,自动化测试无法验证系统不同组件可能交互的所有多种方式。为此,使用接口(从某种意义上说,是组件之间的一种“契约”)成为减少潜在错误的有用工具。并且接口需要编译时类型检查。

我真的很喜欢用动态语言编程,所以我当然不会抨击动态类型。这只是我最近遇到的一个问题。不幸的是,我在将动态语言用于大型复杂系统方面并没有任何经验,所以我很想听听其他人的意见,这个问题是真实的,还是仅仅是理论上的。

于 2009-11-13T06:46:16.287 回答