假设我们有一个定义抽象类型T:
的模块module AbstractType (T, lexer) where
data T = T String deriving Show
lexer = (fmap T) . words
(请注意,我们不会为T导出任何类型构造函数,因此用户将无法手动草拟实例。)
单位测试lexer
如何运作?
当然我们可以使用T的Show属性,如下所示:
module Main where
import AbstractType
main = test
(show $ lexer "summer is miles and miles away")
"[T \"summer\",T \"is\",T \"miles\",T \"and\",T \"miles\",T \"away\"]"
test :: (Eq a) => a -> a -> IO ()
test expression expectation
| expression == expectation = putStrLn "Test passed."
| otherwise = error "Test failed."
- 但是当我们的抽象类型不是允许转换为另一个可构造类型的类的实例时,这既不美观又不适合。
有补救措施吗?
P.S。为案例提供一些理由:假设我们有一系列函数,如parser . lexer
,我们可以进行集成测试,看看它是否有效。由于手头的链条变得更加复杂,因此可能需要单独对每个链路进行单元测试。
该示例是我正在编写的实际玩具文本处理器的简化摘录。
答案 0 :(得分:0)
generally accepted的最佳实践是,对于暴露的模块A
,创建一个内部模块A.Internal
,该内部模块可以是:
暴露,但有记录表明不稳定或不安全。
不暴露给软件包的用户,而仅暴露给测试机构。 (这是Cabal 2.0中发布的internal libraries功能所致。)
据我了解,未公开的功能会享受更为彻底的优化,尤其是内联。我不确定它是否也适用于内部库中的函数。
另一方面,当用户迫切需要您的库的某些内部功能并最终进行分叉和修补以获得访问权时,通常会出现这种情况。当然,这是不幸的和不希望的。
我通常会说,抽象类型的实现最好作为安全措施保存在内部库中,但是您应该在每个特定情况下都要运用自己的判断。