本文首发于 Nebula Graph 公众号 NebulaGraphCommunity,Follow 看大厂图数据库技术实践。python
在上篇文章中,咱们介绍了 Nebula Graph 的集成测试的演进过程。本篇就介绍一下向测试集合中添加一个用例,并成功运行全部的测试用例的过程。ios
在构建 2.0 测试框架之初,咱们定制了部分工具类来帮助测试框架快速地启停一个单节点的 nebula 服务,其中有检查端口冲突、修改部分配置选项等功能。原来的执行流程以下:git
pytest.main
并发执行全部的测试用例;其中的不便之处在于,当须要给 pytest 指定某些参数选项时,须要将该参数透传给pytest.main
函数,而且每次运行单个测试用例须要经过cmake
生成的脚原本操做,不是很方便。咱们但愿“测试用例在哪儿,就在哪儿执行测试”。github
在本次测试框架的改造过程当中,咱们除了改变了程序入口以外,大部分复用了原来封装好的逻辑。因为 nebula 目前积累了不少的用例,单进程运行已经不能知足快速迭代的需求,在尝试了其余并行插件以后,考虑到兼容性,咱们最终选择了 pytest-xdist 插件来加速整个测试流程。正则表达式
可是 pytest 只提供了四种 scope 的 fixture:session,module,class 和 function。而咱们但愿能用一种 global 级别的 fixture 来完成 nebula 服务的启动和初始化。目前最高层次的 session 级别仍是每一个 runner 都要执行一次,如此若是有 8 个 runner 的话,就要启动 8 个 nebula 服务,这不是咱们指望的。数据库
参考 pytest-xdist 的文档,须要经过文件锁来进行不一样 runner 之间的并行控制。为了让控制逻辑足够的简单,咱们把程序启停和预备的逻辑同执行测试的过程分开,使用单独的步骤控制 nebula 的启动,当某些测试有问题时,还能够经过 nebula-console 单独链接测试的服务,进行进一步的验证调试。编程
在此以前,Nebula 的数据导入过程是直接执行一条拼接好的 nGQL INSERT 语句。这样作,存在以下问题:markdown
针对以上的问题,参考nebula-importer的实现,咱们将导入的逻辑和数据集彻底分离,从新实现了 python 版的导入模块。不过,目前只支持导入 csv 类型的数据文件,且每一个 csv 文件中只能存储一个tag/edge
类型。session
重构导入的逻辑以后,目前 nebula 的测试数据集变得清晰明了:数据结构
nebula-graph/tests/data
├── basketballplayer
│ ├── bachelor.csv
│ ├── config.yaml
│ ├── like.csv
│ ├── player.csv
│ ├── serve.csv
│ ├── team.csv
│ └── teammate.csv
├── basketballplayer_int_vid
│ └── config.yaml
└── student
├── config.yaml
├── is_colleagues.csv
├── is_friend.csv
├── is_schoolmate.csv
├── is_teacher.csv
├── person.csv
├── student.csv
└── teacher.csv
3 directories, 16 files
复制代码
每一个目录包含一个 space 中全部的 csv 数据文件,经过该目录下的config.yaml
来配置每一个文件的描述以及 space 的详细信息。经过这份配置信息,咱们也实现了 basketballplayer 和 basketballplayer_int_vid
两个 space 共享同一份数据。之后若是想添加新的测试数据集,只要增长一个相似 basketballplayer 的数据目录便可。config.yaml
的具体内容见repo。
除却经常使用的 pytest 和 nebula-python 库以外,目前的测试框架还用到了 pytest-bdd 和 pytest-xdist 等插件。此外,为了更好地统一添加测试用例 feature 文件的格式,咱们引入了社区的reformat-gherkin工具,并基于此作了部分格式的调整,来保持与 openCypher TCK feature 文件的格式统一。
目前 nebula-python 和 reformat-gherkin 两款插件都是经过源码直接安装,咱们在nebula-graph/tests
下提供了Makefile
来简化用户的操做流程。执行测试的全部环境准备只须要执行命令:
$ cd nebula-graph/tests && make init-all
复制代码
咱们也将上述格式检查集成到了 GitHub Action 的 CI 流程中,若是用户修改的测试文件格式不合预期,可经过make fmt
命令作本地的格式化处理。
由上篇所述,如今 nebula 的集成测试变为“黑盒”测试,用户再也不须要关心本身编写的语句怎么调用,调用什么函数比较符合预期结果。只要按照约定的规范,使用近似“天然语言”的方式在 feature 文件中描述本身的用例便可。如下是一个测试用例的示例:
Feature: Variable length pattern match (m to n)
Scenario: both direction expand with properties
Given a graph with space named "basketballplayer"
When executing query:
"""
MATCH (:player{name:"Tim Duncan"})-[e:like*2..3{likeness: 90}]-(v)
RETURN e, v
"""
Then the result should be, in any order, with relax comparison:
| e | v |
| [[:like "Tim Duncan"<-"Manu Ginobili"], [:like "Manu Ginobili"<-"Tiago Splitter"]] | ("Tiago Splitter") |
复制代码
Given
提供测试的初始条件,这里初始化一个名为 "basketballplayer" 的 space。When
描述测试的输入,即 nGQL 语句。Then
给出指望结果和指望比较的方式,这里表示无序宽松比较表格中的结果。
Feature 文件是 Gherkin 语言描述的一种文件格式,主要由以下几个部分构成:
每一个 Scenario 又分为了避免同的 step,每一个 step 都有特殊的意义:
由上面描述可知,Scenario 就是有一个个的 step 组成,nebula 在兼容 openCypher TCK 的 step 基础上又定制了一些特有的步骤来方便测试用例的书写:
Given a graph with space named "basketballplayer"
:使用预先导入 “basketballplayer” 数据的 space;creating a new space with following options
:建立一个含有以下参数的新的 space,能够指定 name、partition_num、replica_factor、vid_type、charset 和 collate 等参数;load "basketballplayer" csv data to a new space
:向新 space 导入 “basketballplayer” 数据集;profiling query
:对 query 语句执行PROFILE
,返回结果中会含有 execution plan;wait 3 seconds
:等待 3 秒钟,在 schema 相关的操做时每每须要必定的数据同步时间,这时就能够用到该步骤;define some list variables
:定义一些变量表示元素不少的 List 类型,方便在指望结果中书写对应的 List;the result should be, in any order, with relax comparison
:执行结果进行无序宽松比较,意味着指望结果中用户写了什么就比较什么,没写的部分即便返回结果中有也不做比较;the result should contain
:返回结果必须包含指望结果;the execution plan should be
:比较返回结果中的执行计划。除却以上的这些步骤,还可根据须要定义更多的 steps 来加速测试用例的开发。
根据TCK 的描述可知,openCypher 定义了一组图语义的表示方式来表达指望的返回结果。这些点边的格式借鉴了MATCH
查询中的 pattern,因此若是熟悉 openCypher 的查询,基本能够很容易理解 TCK 测试场景中的结果。好比部分图语义的格式以下所示:
(:L {p:1, q:"string"})
;[:T {p:0, q:"string"}]
;<(:L)-[:T]->(:L2)>
。可是 Nebula Graph 同 Neo4J 的在图模型上仍是有一些不一样,好比在 Nebula Graph 中每一个 Tag 都可以有本身的属性,那么按照现有的表述方式是不能描述含有多个带属性 Tag 的 vertex 的。在边的表示上也有差别,Nebula Graph 的 Edge Key 是由四元组组成<src, type, rank, dst>
,而现有的表示也不能描述边的 src、dst 和 rank 的值。故而在考虑了这些差别以后,咱们扩充了现有 TCK 的 expected results 表达:
("VID" :T1{p:0} :T2{q: "string"})
;[:type "src"->"dst"@rank {p:0, q:"string"}]
;经过上述的点边描述方式上的扩充,即兼容 TCK 现有用例,又契合了 Nebula Graph 的设计。在解决了表达方式上的问题后,面临的下一个问题是如何高效无误地转化上述的表示到具体的数据结构,以便可以跟真正的查询结果作比较。在考虑了正则匹配、parser 解析等方案后,咱们选择构造一个解析器的方式来处理这些具备特定语法规则的字符串,这样作的好处有以下的几点:
借助ply.yacc 和 ply.lex 两个 library,咱们能够用少许的代码实现上述复杂的需求,具体实现见nbv.py 文件。
目前的测试流程变为:
目前 Nebula Graph 全部的 feature 用例均位于 github.com/vesoft-inc/nebula-graph repo 中的tests/tck/features目录中。
$ cd /path/to/nebula-graph/tests
$ make up # 启动 nebula graph 服务
复制代码
$ make fmt # 格式化
$ make tck # 执行 TCK 测试
复制代码
$ mak
e down
复制代码
当编写的用例须要调试时,即可以使用 pytest 支持的方式来进一步的调试,好比从新运行上次过程当中失败的用例:
$ pytest --last-failed tck/ # 运行 tck 目录中上次执行失败的用例
$ pytest -k "match" tck/ # 执行含有 match 字段的用例
复制代码
也能够在 feature 文件中对特定的一个 scenario 打上一个 mark,只运行该被 mark 的用例,好比:
# in feature file
@testmark
Scenario: both direction expand with properties
Given a graph with space named "basketballplayer"
...
# in nebula-graph/tests directory
$ pytest -m "testmark" tck/ # 运行带有 testmark 标记的测试用例
复制代码
站在前人的肩膀之上才让咱们找到更适合 Nebula Graph 的测试方案,在此也一并感谢文中提到的全部开源工具和项目。
在实践 pytest-bdd 的过程当中,也发现其中一些不完美的地方,好比其跟 pytest-xdist 等插件兼容性的问题(gherkin-reporter),还有 pytest 没有原生提供 global scope 级别的 fixture 等。但终究其带给 Nebula Graph 的收益要远大于这些困难。
上篇中有提到不须要用户进行编程,并不是凭空想象,当咱们把上述的模式固定后,能够开发一套添加测试用例的脚手架,让用户在页面上进行数据“填空”,自动生成对应的 feature 测试文件,如此即可进一步地方便用户,此处能够留给感兴趣的社区用户来作些尝试了。
交流图数据库技术?加入 Nebula 交流群请先填写下你的 Nebula 名片,Nebula 小助手会拉你进群~~
想要和其余大厂交流图数据库技术吗?NUC 2021 大会等你来交流:NUC 2021 报名传送门