乐趣区

关于bdd:基于-BDD-理论的-Nebula-集成测试框架重构下篇

本文首发于 Nebula Graph 公众号 NebulaGraphCommunity,Follow 看大厂图数据库技术实际。

在上篇文章中,咱们介绍了 Nebula Graph 的集成测试的演进过程。本篇就介绍一下向测试汇合中增加一个用例,并胜利运行所有的测试用例的过程。

环境筹备

在构建 2.0 测试框架之初,咱们定制了局部工具类来帮忙测试框架疾速地启停一个单节点的 nebula 服务,其中有查看端口抵触、批改局部配置选项等性能。原来的执行流程如下:

  1. 通过 python 脚本启动 nebula 的服务;
  2. 调用 pytest.main 并发执行所有的测试用例;
  3. 进行 nebula 的服务。

其中的不便之处在于,当须要给 pytest 指定某些参数选项时,须要将该参数透传给 pytest.main 函数,并且每次运行单个测试用例须要通过 cmake 生成的脚本来操作,不是很不便。咱们心愿“测试用例在哪儿,就在哪儿执行测试”。

服务启动

在本次测试框架的革新过程中,咱们除了扭转了程序入口之外,大部分复用了原来封装好的逻辑。因为 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 语句。这样做,存在如下问题:

  1. 测试数据集大的状况,INSERT 语句会变得简短,client 执行超时;
  2. 不易拓展新的测试数据集,须要将现成的 csv 数据文件结构成对应的 nGQL 语句文件;
  3. 不能复用雷同的数据集,比方心愿同一份 csv 导入到不同 VID 类型的 space 中测试,须要结构不同的 INSERT 语句。

针对以上的问题,参考 nebula-importer 的实现,咱们将导入的逻辑和数据集齐全拆散,从新实现了 python 版的导入模块。不过,目前只反对导入 csv 类型的数据文件,且每个 csv 文件中只能存储一个 tag/edge 类型。

重构导入的逻辑之后,目前 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 文件构造

Feature 文件是 Gherkin 语言形容的一种文件格式,次要由如下几个局部形成:

  • Feature:能够增加以后文件的“Title”,也能够详细描述文件内容;
  • Background:后续 Scenario 独特应用的步骤;
  • Scenario:由一个个步骤形容每个测试用例的场景;
  • Examples:能够进一步将测试场景和测试数据进行拆散,简化以后 Feature 文件中 Scenarios 的书写;

每个 Scenario 又分为了不同的 step,每个 step 都有非凡的意义:

  • Given: 设置以后测试场景的初始条件,上述 Background 中只能含有 Given 类型的 step;
  • When: 给定以后测试场景的输出;
  • Then: 形容做完 When 的 step 后冀望失去的后果;
  • And: 能够紧跟上述 Given/When/Then 中任何一种类型的 step,进一步补充上述的 step 的动作;
  • Examples: 相似上述 Examples 形容,不过作用的范畴限定在单个 Scenario 中,不影响同 Feature 文件中的其余 Scenario 测试。

    Steps

由下面形容可知,Scenario 就是有一个个的 step 组成,nebula 在兼容 openCypher TCK 的 step 根底上又定制了一些特有的步骤来不便测试用例的书写:

  1. Given a graph with space named "basketballplayer":应用事后导入“basketballplayer”数据的 space;
  2. creating a new space with following options:创立一个含有如下参数的新的 space,能够指定 name、partition_num、replica_factor、vid_type、charset 和 collate 等参数;
  3. load "basketballplayer" csv data to a new space:向新 space 导入“basketballplayer”数据集;
  4. profiling query:对 query 语句执行PROFILE,返回后果中会含有 execution plan;
  5. wait 3 seconds:期待 3 秒钟,在 schema 相干的操作时往往须要肯定的数据同步工夫,这时就能够用到该步骤;
  6. define some list variables:定义一些变量示意元素很多的 List 类型,不便在冀望后果中书写对应的 List;
  7. the result should be, in any order, with relax comparison:执行后果进行无序宽松比拟,意味着冀望后果中用户写了什么就比拟什么,没写的局部即便返回后果中有也不作比拟;
  8. the result should contain:返回后果必须蕴含冀望后果;
  9. the execution plan should be:比拟返回后果中的执行打算。

除却以上的这些步骤,还可依据须要定义更多的 steps 来减速测试用例的开发。

Parser

依据 TCK 的形容可知,openCypher 定义了一组图语义的示意形式来表白冀望的返回后果。这些点边的格局借鉴了 MATCH 查问中的 pattern,所以如果相熟 openCypher 的查问,根本能够很容易了解 TCK 测试场景中的后果。比方局部图语义的格局如下所示:

  1. 点的形容:(:L {p:1, q:"string"});
  2. 边的形容:[:T {p:0, q:"string"}];
  3. 门路的形容:<(: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 表白:

  1. 点的形容反对带属性的多个 Tag:("VID" :T1{p:0} :T2{q: "string"})
  2. 边的形容反对 src、dst 和 rank 的示意:[:type "src"->"dst"@rank {p:0, q:"string"}]
  3. 门路就是循环上述点边的示意即可,同 TCK。

通过上述的点边形容形式上的裁减,即兼容 TCK 现有用例,又符合了 Nebula Graph 的设计。在解决了表达方式上的问题后,面临的下一个问题是如何高效无误地转化上述的示意到具体的数据结构,以便可能跟真正的查问后果做比拟。在思考了正则匹配、parser 解析等计划后,咱们抉择结构一个解析器的形式来解决这些具备特定语法规定的字符串,这样做的益处有如下的几点:

  1. 能够依据具体的语法规定让解析进去的 AST 合乎查问返回后果的数据结构,两者再进行比拟时,便是具体构造中的具体字段的校验了;
  2. 防止解决简单的正则匹配字符串,缩小解析的谬误;
  3. 能够反对其余字符串解析的需要,比方正则表达式、列表、汇合等

借助 ply.yacc 和 ply.lex 两个 library,咱们能够用大量的代码实现上述简单的需要,具体实现见 nbv.py 文件。

测试流程

目前的测试流程变为:

1) 编写 Feature 文件

目前 Nebula Graph 所有的 feature 用例均位于 github.com/vesoft-inc/nebula-graph repo 中的 tests/tck/features 目录中。

2) 启动 nebula graph 服务

$ cd /path/to/nebula-graph/tests
$ make up # 启动 nebula graph 服务

3) 本地执行测试

$ make fmt # 格式化
$ make tck # 执行 TCK 测试

4)进行 nebula graph 服务

$ 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 报名传送门

退出移动版