我试图在haskell中编写一些简单的代码,其中有一个函数执行简单的数据库查询。为了单元测试我使用HUnit,但不确定如何模拟数据库连接和查询响应。
将执行数据库查询的函数作为参数传递给代码,而不是让它"硬接"。在测试期间,将模拟函数传递给代码。在生产环境中,传递一个真正执行数据库查询的函数。
这个新的函数参数应该处于正确的抽象级别。例如,像
queryDbForClient :: Connection -> SQLString -> ClientId -> IO (Maybe RowData)
可能是一个坏主意,因为它仍然迫使被测代码意识到存在Connection
和SQL字符串之类的东西。像这样
findClientById :: ClientId -> IO (Maybe Client)
可能更好,因为这样您可以自由地传递根本不使用db特定的Connection
类型的函数。例如,它们可以由内存中的引用来支持。
注意,您可以通过部分应用程序从queryDbForClient
构建findClientById
并稍微映射结果。但是这应该是一些setup代码的任务,而不是您想要测试的主代码的任务。
一旦为了可测试性和可配置性开始以这种方式传递函数,就会出现一些常见问题。例如,如果我有多个依赖项怎么办?将它们全部作为位置参数传递是件麻烦事。这可能导致将它们一起传递到函数记录中,可能使用Has
类类型类,以便不将主代码绑定到任何特定的记录。