在haskell中实现生成测试用例的构建器模式
我需要为用Java编写的应用程序生成cucumber测试用例 测试用例如下所示:在haskell中实现生成测试用例的构建器模式,haskell,code-generation,Haskell,Code Generation,我需要为用Java编写的应用程序生成cucumber测试用例 测试用例如下所示: Scenario My great test Given the following input """ Code snippet of a DSL """ And the following data | name | type | value | | a | Boolean | true |
Scenario My great test
Given the following input
"""
Code snippet of a DSL
"""
And the following data
| name | type | value |
| a | Boolean | true |
| b | Integer | 5 |
When I run the evaluation
Then the result should be "Yay!"
data TestCase = Scenario String DslStatement DataStatement ResultStatement
data DslStatement = Dsl [TopLevelStatement]
data TopLevelStatement =
StatementTypeA String
| StatementTypeB String
| StatementTypeC String SubStatementTypeA [SubStatementTypeB]
| StatementTypeD String [String]
...
我已经创建了类似于这个结构的数据类型,如语法树以及一个“后端”,它将接受语法树并创建测试用例字符串
数据类型如下所示:
Scenario My great test
Given the following input
"""
Code snippet of a DSL
"""
And the following data
| name | type | value |
| a | Boolean | true |
| b | Integer | 5 |
When I run the evaluation
Then the result should be "Yay!"
data TestCase = Scenario String DslStatement DataStatement ResultStatement
data DslStatement = Dsl [TopLevelStatement]
data TopLevelStatement =
StatementTypeA String
| StatementTypeB String
| StatementTypeC String SubStatementTypeA [SubStatementTypeB]
| StatementTypeD String [String]
...
等等
现在,我想使用不同的值、类型和内容生成大量的数据结构
我可以编写一些函数来获取必要的参数,并创建一个语法树,其中的值来自插入到它们应该出现的位置的参数。然而,由于测试用例中包含的DSL随时都可能更改(它是以增量方式开发的),因此我必须始终更改创建不同测试用例类型的所有函数,这是很乏味的。此外,测试用例可以基于标准语法树,对于大多数测试用例,该语法树只能在少数地方修改
我现在的想法是创建或多或少类似于Java中具有流畅接口的构建器模式的函数。从标准语法树开始,我创建了修改该语法树的函数,并返回要进一步修改的结果树,如下所示:
withName :: String -> TestCase -> TestCase
withName name (Scenario _ dsl data result) = Scenario name dsl data result
withResult :: ResultStatement -> TestCase -> TestCase
withResult result (Scenario name dsl data _) = Scenario name dsl data result
...
withName "My Test Case" . withResult (Result "Yay!") $ createStandardTestCase
那么我应该可以写这样的东西:
withName :: String -> TestCase -> TestCase
withName name (Scenario _ dsl data result) = Scenario name dsl data result
withResult :: ResultStatement -> TestCase -> TestCase
withResult result (Scenario name dsl data _) = Scenario name dsl data result
...
withName "My Test Case" . withResult (Result "Yay!") $ createStandardTestCase
只要dsl发生变化,就只需修改构建器函数和后端,以适应我的测试用例
这是解决问题的可能/有效方法吗?
有没有更好的办法来创建这样的语法树
谢谢
--Mathias.流畅的界面模式在Haskell中称为
Endo
。它是一个Monoid
,因此使用mconcat
可以获得一些效率,尽管我很少在实践中使用Endo
,因为它不是一个巨大的收益
使用这样的定义,您将面临的一个挑战是需要默认的一切,毕竟名为“My Test Case”的本身需要是一个有效的TestCase
。这可能意味着您的许多类型将是可能是
s,也可能只是意味着您需要仔细定义您的类型。这可能与标准语法树的概念有关
创建这样一个可扩展AST的全功能方法是使用这些技术。简而言之,您定义一个通用的“sum”类型运算符,然后构建对递归类型的某些组件的和进行操作的函数。通过使用巧妙的默认值,您可以省略许多样板定义,并允许扩展性
这些技术可能对您的类型有用
最后,在讨论像这样的嵌套数据类型时,如果不提出通过(包括所有可能的电池)或(更简单)查看镜头的建议,就很难做到这一点。这样,您就可以对树进行深入检查,双向使用这些树来查看和构建更新Endo
s。它们也有聪明的通用原则,比如能够同时“关注”树中的多个位置(这是控件中的折叠s和遍历s)。这和你的想法接近吗?