本文首發於 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 報名傳送門