GraphQL 概念與測試(下):graphy測試框架

2021-02-14 字節質量保障團隊

作者:皮皮哥

上一篇文章中,我們介紹了graphql的概念以及優劣勢。基於前一篇文章,我們就來聊一聊graphql應該怎麼測,它的官方python客戶端sgqlc該怎麼用,以及介紹一下我自己開發的graphy測試框架。

 舉個例子

首先讓我們來看一個graphql請求與響應的例子。

請求:

query {  MGetMainAccelerDomain(ids: ["1"]) {    id    name    core {      business_type      is_main      owner    }    distribute {      items {        domain {          id          name        }      }    }  }}

響應:

{  "data": {    "MGetMainAccelerDomain": [      {        "id": "1",        "name": "www.baidu.com",        "core": {          "business_type": "",          "is_main": false,          "owner": ""        },        "distribute": {          "items": []        }      }    ]  }}

我們會發現:

graphql雖然自由度很高,但還是定義了請求入口,在這裡是query下的MGetMainAccelerDomain,它的返回是AccelerDomain的數組。

它的自由度體現在,請求時可能要求這個對象的不固定的欄位(可以是全部,也可以只是必選)。

它的自由度還體現在,請求欄位可能有下級欄位繼續拓撲,甚至可能出現遞歸,例如這裡的domain.distribute.items[]又是domain類型了。

 graphql測試思路

於是我們可以歸納graphql接口的測試思路:

 預期的graphql測試框架

然後讓我們思考一下,一個合理的graphql測試框架應該需要哪些功能。

 sgqlc:官方的python客戶端

sgqlc是graphql推薦的官方python客戶端,它的github地址是:https://github.com/profusion/sgqlc。在這裡我就用官方的例子來說明它的能力:

【爬取模型與生成模型類】

python3 -m sgqlc.introspection \--exclude-deprecated \--exclude-description \-H "Authorization: bearer ${GH_TOKEN}" \https://api.github.com/graphql \github_schema.json
sgqlc-codegen github_schema.json github_schema.py

這樣生成的模型類就是github_schema了。

【構造請求與發送】

from sgqlc.operation import Operationfrom github_schema import github_schema as schema
op = Operation(schema.Query)
issues = op.repository(owner=owner, name=name).issues(first=100)issues.nodes.number()issues.nodes.title()issues.page_info.__fields__('has_next_page')issues.page_info.__fields__(end_cursor=True)
print(op)
data = endpoint(op)
repo = (op + data).repositoryfor issue in repo.issues.nodes: print(issue)

【sgqlc的問題】

使用的過程中我遇到幾個問題,或者說不順手的地方:

 graphy:自研的graphql測試框架

於是,我便開始嘗試自己開發一個自己覺得順手的graphql測試框架(using python),我給它起名graphy。讓我們先看一個demo。

【模型與生成】

python3 graphy/graphy_gen.py "http://10.227.10.13:7794/graphql"\ http_test/graphy_test/example.py

生成的模型類如下:

入口類有Query和Mutation兩種。入口類中會用_fields來記錄它可以進行操作,以及對應的返回類型。

class Query(graphy.Query):    _description = "xxx"    _fields = {        "FilterSubDomain" : "_listof_SubDomain",        "GetSubDomain" : "SubDomain"    }    def __init__(self, schema=None, depth=None):        self.FilterSubDomain : List[SubDomain]        self.GetSubDomain : SubDomain        return super().__init__(schema=schema, depth=depth)

同理,一個節點類會使用_fields的靜態字典來記錄它具備的欄位/類型;同時也生成了__init__方法,會自動為對象賦予對應的欄位,以及變量類型。

class AuthInfo(graphy.Node):    _description = "認證信息"    _fields = {        "id" : "int",        "name" : "str",        "description" : "str",        "params" : "_listof_AuthParam",    }    def __init__(self, args=None):        self.id : int        self.name : str        self.description : str        self.params : List[AuthParam]        return super().__init__(args=args)

如果遇到了數組類型,我會用一個_listof_xxx的類型來描述:

class _listof_AuthParam(graphy.List):    _description = "List type of AuthParam"    _item_type_name = "AuthParam"

同時也處理了INPUT,Enum等類型。

所有的類型都可以不斷向下拓撲,直到遇到了基礎類型,也就是STRING,INT,FLOAT,BOOL,ID;它們會被對應成python的基礎類型。

至此,我們解決了第一個問題,所有的graphql節點你都知道它的類型是什麼,並且在IDE中可以hint。

【構造請求】

query = Query(schema="http_test.graphy_test.example_schema", depth=1)query.MGetMainDomain({"ids": [79417]})

print(query._g())
query { MGetMainDomain(ids: [79417]) { id name }}

query = Query(depth=2)  query.MGetMainDomain({"ids": [79417]})._d(2)  #要求在這個節點下拓撲2層

query.MGetMainDomain._f(["id"])

query.MGetMainDomain._a({"ids": [79417]})query.MGetMainDomain({"ids": [79417]})

query.MGetMainDomain._d(2)._a({"ids": [79417]})

其他能力,例如Union,Variables等的使用,這裡就不再展開了。

【獲取響應並反序列化】

client = graphy_client.Client(url="xxx")
query = Query(schema="xxx", depth=2)domains = query.MGetMainDomain({"ids": [79417]})print(query._g())
client.send(query)
print(query)'''"MGetDomain":[ { "name": "www.baidu.com", "id": 171 }, ...]'''
print(domains[0].name)'''www.baidu.com'''

至此,我們解決了第二個問題,就是響應結構體無法被反序列為對象的問題;我們還額外實現了,query這個對象即是請求對象,又是響應對象。

 graphy的實現思路

這裡分享一下graphy的實現思路,僅供大家參考。

【對象初始化】

首先,在對象初始化時,graphy會為該對象維護兩種信息:

【請求與響應】

在實際請求時,一個請求的隱變量以及它的遞歸子節點,會被用來生成特殊格式的graphql請求字符串;當被測graphql接口返回後,這個響應的json字典,會被反序列化並回填到可見變量中。

這樣我們就實現了請求和響應使用同一個對象,並且外層並不需要感知這些,使用起來沒有負擔。


 後續todo

graphy目前還是我們項目組自用的框架,由於代碼寫的有點醜陋目前還沒有提交給公司作為公共pip包;很多東西其實你不寫一個亂糟糟的第一版,你是不知道怎麼才能寫得更優雅的。

實際上還有一些graphql的特性我沒有支持,例如Aliases,Fragments,Directives等等。

由於graphy可以感知模型,並且請求和響應應該是全量並且一致的,所以graphql非常適合做測試代碼自動生成,以及流量錄製回放測試;這個我在後面也會進行嘗試。

相關焦點

  • GraphQL|一種配得上凡爾賽的API框架
    在知道答案之前,我們先來了解以下graphql。以下是graphql的官方站:https://graphql.cn/通過官方的實例,我們可以知道,graphql的主要功能,是進行API測試,與其他的API測試工具相比,graphql有幾個優勢:1、graphql可以通過圖形化界面的方式,在同一個界面上交互式地進行API數據測試,
  • GraphQL入門指南
    ReferenceTutorials & DocsMechanism:原理介紹Practices & ResourcesComparison:框架對比CollectionQuick StartOfficial Quick Start:官方的簡單的Quick Start教程Setup首先創建項目文件夾:mkdir graphql-democd graphql-demo
  • GraphQL 簡介:原理及其使用
    在代碼中啟用 GraphQLGraphQL 有一個名為 /graphql 的單一的 URL 資源路徑,它將處理所有的請求。 使我們能夠在 /graphql url 中設置 GraphQL 伺服器,它知道如何處理即將發生的請求。
  • GraphQL 從入門到實踐
    正文從這開始~~本文首先介紹了 GraphQL,再通過 MongoDB + graphql + graph-pack 的組合實戰應用 GraphQL,詳細闡述如何使用 GraphQL 來進行增刪改查和數據訂閱推送,並附有使用示例,邊用邊學印象深刻~0. 什麼是 GraphQLGraphQL 是一種面向數據的 API 查詢風格。
  • GraphQL 基礎實踐
    聯合類型(Union)聯合類型與接口概念差不多相同,不同之處在於聯合類型下的類型之間沒有定義公共的欄位。在 Union 類型中必須使用內聯片段的方式查詢,原因與上面的接口類型一致。union SearchResult = Song | VideoQuery {    search(term: String!)
  • Apollo GraphQL 在 webapp 中應用的思考
    app.use('/graphql', graphqlExpress({ schema }));app.use('/graphiql', graphiqlExpress({ endpointURL: '/graphql'})); if (process.env.NODE_ENV === 'development')
  • GraphQL 入門看這篇就夠了
    graphql + graph-pack 的組合實戰應用 GraphQL,詳細闡述如何使用 GraphQL 來進行增刪改查和數據訂閱推送,並附有使用示例,邊用邊學印象深刻~如果希望將 GraphQL 應用到前後端分離的生產環境,請期待後續文章。
  • GraphQL 實戰篇之前端Vue+後端
    🎉🎉🎉前面我們介紹了GraphQL的概念和基礎知識,這篇文章記錄下使用Nestjs+GraphQL搭建Node服務。安裝npm i --save @nestjs/graphql graphql-tools graphql apollo-server-express註冊// app.module.tsimport { Module } from '@nestjs/common
  • 【超詳細教程】如何使用TypeScript和GraphQL開發應用
    fantingshengdeMacBook-Pro:graphql-typescript fantingsheng$ yarn inityarn init v1.12.3question name (graphql-typescript):question version (1.0.0):question description: for study
  • 如何使用GraphQL-進階教程:客戶端
    根據開發平臺和框架的不同,通常會有不同的方法來處理 UI 更新。以 React 為例,GraphQL 客戶端使用高階組件(higher-order components)的概念來獲取所需的數據,並且使它在組件的 props 中可用。通常,GraphQL 的聲明特性與函數響應式編程技術相關性較高。
  • 淺談BDD下的自動化測試框架
    引言:測試驅動開發(TDD)相信大家已經很熟悉了,而行為驅動開發(BDD)其實是TDD的一種演化。那什麼是BDD,為什麼要使用BDD, BDD下的自動化測試該如何做呢?目錄:一、什麼是BDD二、為什麼要使用BDD三、常用的BDD測試框架四、BDD自動化測試框架Cucumber
  • 軟體測試學習教程:單元測試之UnitTest測試框架
    單元測試的概念單元測試(unit testing),是指對軟體中的最小可測試單元進行檢查和驗證。對於單元測試中單元的含義,要根據實際情況去判定其具體含義。單元測試工具不同的程式語言都有比較成熟的單元測試框架,語法規則有些差別,其核心思想都是相通的。
  • GraphQL 值得了解一下
    本文參考資料:https://medium.com/javarevisited/basic-functionalities-of-graphql-every-developer-should-know-71347d7fab08
  • Python unittest單元測試框架的使用
    一起跟隨小編過來看看吧一、測試模型下面這部分來自於某書籍資料,拿過來,按需參考一下:測試模型(1)線性測試1、概念:通過錄製或編寫對應應用程式的操作步驟產生的線性腳本。4.線性測試實例:用戶登錄(2)模塊化驅動測試1、概念:將重複的操作獨立成功共模塊,當用例執行過程中需要用到這一模塊操作時則被調用。操作+(重複操作,數據)混合在一起。
  • 寫在2021: 值得關注/學習的前端框架和工具庫
    一體化框架指的是, 你的前後端項目放在同一個repo裡(後端是Node),同時前端直接調用在後端定義的方法,由框架在編譯時去自動的把前端對後端的方法調用轉換成HTTP請求。這是最近前端還挺火熱的一個方向,主要的基於Node的一體化框架主要有這麼幾個:[1]學完Vue還有必要學習React和Node嗎?
  • 細說unittest單元測試框架
    一、單元測試框架說明 單元測試是指在編程中,針對程序模塊的最小單元(類中的方法)進行正確性檢驗的測試工作。python+selenium自動化測試中通常使用unittest或者pytest作為單元測試框架。
  • 自動化測試框架結構圖
    點擊上方"AllTests軟體測試",設為星標第一時間關注技術乾貨!
  • 測試用例編寫(功能測試框架)
    測試用例的編寫需要按照一定的思路進行,而不是想到哪寫到哪,一般測試機製成熟的公司都會有公司自己自定義的測試用例模板,以及一整套的測試流程關注點,當然我們自己在測試生涯中也應當積累一套自己的測試框架,所有功能性的測試都可以依據框架的思路來進行,達到事半功倍的效果。
  • 自動化測試基礎篇:Selenium 框架設計(POM)
    自動化測試框架能夠提供很多便利給用戶高效完成一些事情。框架具有以下一些優點:1)代碼復用2)最大覆蓋率3)很低成本維護4)很少人工幹預5)簡單報告輸出2.常見的測試框架分類1)基於模塊的測試框架2)基於庫(Library)結構測試框架3)數據驅動測試框架,和QTP很像
  • Netflix 聯邦 GraphQL 平臺的實現過程及經驗教訓
    最初,有很多人持懷疑態度並有異議;這個概念相當新,需要整個組織高度一致才能成功。我們的團隊花了很多時間來解決不同意見,並根據開發人員的反饋對架構進行了調整。通過我們的原型開發以及與一些關鍵批評聲音的積極合作,我們能夠逐漸增加信心並彌合關鍵差距。一旦我們在這個想法上達成了廣泛的一致,我們就需要確保其採用是無縫的。