是否有用于编译时检查的示例值很常见,它们应该在代码中的位置?



我有一个相当复杂的数据类型和记录结构,对于不熟悉代码库的人来说,仅仅通过查看生产代码并不容易理解。

为了理解它,我创建了这种虚拟函数,它有两个优点:1.它们在编译时被检查,2.它们用作某种文档,展示了数据类型和记录的整体结构如何混合在一起的示例:

-- benefit 1: a newcomer can quickly make sense of the type system
-- benefit 2: easier to keep track of how the types evolve because of compile-time checking
exampleValue1 :: ApiResponseContent
exampleValue1 = ApiOnlineResponseContent [
OnlineResultRow (EntityId 10) [Just (FvInt 1), Just (FvFloat 1.5), Nothing],
OnlineResultRow (EntityId 20) [Just (FvInt 2), Nothing, Just (FvBool True)]
]

唯一让我有点困扰的是,它们在生产代码中感觉有点尴尬,因为它们显然是死代码。但是,它们也不是测试,它们只是经过编译的时间检查示例,说明如何将值从复杂的嵌套类型组合在一起。因此,它们显然不属于生产代码,但它们也不完全属于测试。

拥有这种编译时示例是常见的做法吗?它们应该放在代码库中的什么位置?

您是否考虑过将这些示例作为 Haddock 文档的一部分?


否则,有一种思想流派试图将测试重新构建为示例。你可以在 Gerard Meszaros 关于单元测试的工作中找到对此的简要提及。丹·诺斯(Dan North)也多次使用类似的语言,但通常很难追踪他对此类想法的多次迭代。他倾向于"在公共场合思考"——这里有一个这样的反思:

"我称之为开发,使用示例引导设计">

我知道被问到的例子并不完全是这样的。不过,我认为它更适合测试代码而不是生产代码。

如果将示例放在生产代码中,则实际上使其成为库 API 的一部分(如果您要交付库)。这意味着更改示例将构成重大更改。这对我来说似乎不对。

在 BDD/DDD 社区中,非常强调测试作为示例,也是在自动化测试用作文档的意义上。如果 Haddock 文档不是一种选择,我会考虑将这些示例作为文档放在测试代码中。我有时只是简单地将这种"空洞的测试"放在一个名为examples的文件中,也许在顶部有一些注释来解释该文件中的代码有助于学习而不是验证行为。

它避免了在生产代码中引入冗余中断性更改的风险,并且在概念上似乎更适合。

我不同意这些不是测试。甚至"此示例是否编译"也可以被视为一个测试,但您可能也可以在使用它们来实际测试某些函数时。

因此,将这些定义放在测试套件中,并使用它们对实际处理此类值的函数进行单元测试。

我看到的容纳此类示例的主要约定是包含….Tutorial模块,例如Dhall.TutorialClash.Tutorial,或并行…-tutorial包,例如lens-tutorial

这些包含 Haddock 文档,组织起来以便按线性顺序阅读,以及像您这样的示例定义。

通过很好的例子,我发现除了类型和参考文档之外,这个约定对于理解和试验新包非常有帮助。在 Hackage 上浏览软件包时,它也相当容易被发现,特别是如果您确保参考和自述文件链接到教程以获取更长的解释。

这些模块可以编译为基本验证,但是为文档提供更多的机器验证是非常有价值的,所以我认为将它们链接到测试套件中会更好(例如hspec)作为单元测试(HUnit)或属性测试(QuickCheck/hedgehog)的例子,或将它们组织为文档测试(doctest)。

除了 GHC 的代码覆盖率工具外,weeder还可以帮助识别未测试的示例。

最新更新