1. 程式人生 > >檢查數據接口返回數據合法性

檢查數據接口返回數據合法性

bsp nbsp function return als 個數 require members 根據

問題背景:

  在測試&部署監控過程中,我們常常會遇到外部接口返回數據不靠譜的時候。最常見的場合是從某個http獲取如json和xml等結構化的結果,進行解析並處理,在這時候出現以下這幾種常見類型的錯誤:

1)整個結構不完整。直接無法解析json/xml

技術分享

2)編碼錯誤,常見的gbk/utf8錯誤

技術分享

3)超長數據/非法字符。

技術分享

4)數據類型不匹配。需要是數字的給了字符串,該是數組的給了字符串等,對json本身來說沒問題,程序處理就會錯誤或者崩潰。

5)字段缺失或者為空,這個情況對json本身來說也是沒問題的,處理進程固定要去取這裏的字段就會出問題,或者進程本身沒問題,但實際展現出問題。

例如,json描述一個商品最近30天的售價,提供一個數組裏有30個數據來畫點,json裏這個數組為空,從數據格式上來說沒問題,但實際畫點時展現即為空。
截圖是來自一份合作方的數據,箭頭指向的是上證指數曲線的點,如果點數據完全缺失(為空)則畫曲線的界面會顯示為空。在json結構上則仍然驗證為合法。

技術分享

解決問題的現狀:

  對上述問題,我們有一些簡單的自動化監控手段,通過定期抓取http接口再獲取其中內容這一步比較簡單,接下來我們會驗證http狀態碼(200正常,非200認為是有問題)和長度,如果過短(例如少於20字節)則認為是無效。
  還有一些自動化case會先人工看一下接口返回的具體內容,然後再作為case檢查點,檢查抓取接口中是否存在固定字符串片段,以此進一步驗證返回結果的正確性。這裏我們仍然進行的是字符串粒度上的檢查。


解決問題思考:
  如果只通過字符串的方式檢測長度,這個粒度過於粗糙,難以發現上述詳細一些問題。檢查固定的字符串片段存在,能部分彌補上述不足,但仍然無法檢查背景中提到的諸如字段缺失的問題。
  但是若要檢查字符串完全相同,則要求接口每次返回的數據完全相同,和實際應用情況不符,維護case難度大。那麽,我們的思路就轉向如何對數據結構體進行檢查。

總的思路出於以下幾條:

1.根據歷史接口請求結果作為範本,提取特征作為約束對新數據進行檢查。提高工具本身的適用範圍。

2.接口返回的數據應該可以化為結構體,通過對結構體字段的條件約束來判斷數據檢查是否通過。

3.對有些字段內容可以進行二次解析,保障其中數據的合法性。

4.基於歷史數據特征進行統計,輔助對數據字段進行正確性檢查,進一步降低維護條件約束的難度。

接下來我們逐步來設計我們以上思考功能。

解決問題的設計:

0.用什麽代碼?

  現在任何一種主流語言都有成熟的json/xml解析庫,幸運的是作為正確性檢查,這個規模我們基本不需要考慮性能問題。當速度不可接受的時候可以考慮多進程分開跑來環節壓力。
推薦腳本語言python/php,開發起來更快一些。作為一個輕量的檢查工具甚至只是個lib,它應該盡量簡單,方便使用,方便納入現有的測試框架之中。

1.如何抽取特征?  

  無論是json還是xml,本質上都是樹形結構。基於我們最終目的是進行驗證的考慮,把json/xml解析為樹是個不錯的主意。既然預期結構完全相同,那麽我們可以依照key,依次遍歷檢查樹的每個節點,比對被比較object是否一致。
json為例,我們輸入兩個json字符串,將其轉化為object。如果是復雜結構,則再對object進行遞歸比較,如果是int或者string,則按既定規則比較。

function compare_json($in_json, $diff_json){

    $json = new Services_JSON();

    $start_object = $json->decode($json_str_from_file);

    $diff_object = $json->decode($json_str_diff_from_file);

    return compare_all($start_object, $diff_object);

}

  compare_all(obj2) 進行遍歷,先判斷類型再進行比較,返回布爾值檢查結果向上傳遞。
如圖,我們舉個例子,查詢某個小組成員的數據,根據json生成的樹

{ 

   "name":"name1",

   "date":123,

   "members":[ 

      { 

         "name":"alice",

         "level":5,

         "lastpost":{ 

            "title":"noname1",

            "date":500

         }

      },

      { 

         "name":"bob",

         "level":0,

         "lastpost":{ 

            "title":"noname2",

            "date":501

         }

      }

   ]

}

對應的結構

技術分享

2.約束條件都有哪些類型?

  根據實際應用情況的不同,可以按需求調整。特別是數組由於數量一般不一致,可以考慮在非空的條件下只取第一個進行結構驗證,也可以考慮遍歷取每一個節點進行相同的結構驗證。
在這裏舉一些例子作為常見的檢查參考:

(1)根據key進行遍歷,如果對照的測試數據直接取不到key對應的object,則認為有問題。在取到數據的情況下進行比較:

(2)兩邊數據類型一致

(3)樣板數據非空的情況下,檢查數據應該非空

(4)樣板數據為空,檢查數據為非空或空都可以

(5)數組裏的元素個數應該保持一致

(6)如果不是葉子節點,它下面還有某種結構的話,用相同規則處理下面的子樹。

3.對其中個別節點的附加檢查方式:

  我們同樣考慮,在一些case的執行中,要求對其中一些重要的節點做出復雜檢查操作,這些檢查操作是根據case來的,不能適用到其他case上。最重要的問題是如何定位節點。
  例如上述每條json數據中,希望檢查每個level都在5以上… 通過path定位需要一個輔助工具來索引json返回對應的數據。
對於xpath for json,php有第三方的lib提供了jsonpath來執行類似xpath的檢索功能。通過實驗檢查可行,也可以單獨根據xpath的語法寫一個解析器,但這裏不符合我們對快速實現最終檢查目的這個目標。是否自己寫lib來支持,取決於工期的長短。
http://goessner.net/articles/JsonPath/
一個簡單的實例,對每個level進行檢查。

//load library

require_once (‘lib/JSON.php‘);

require_once (‘lib/jsonpath-0.8.1.php‘);



$json_newstr = ‘{"name": "name1", "date": 123, "members": [{"name":"alice", "level": 5, "lastpost": {"title": "noname1", "date": 500}} , {"name":"bob", "level": 0, "lastpost": {"title": "noname2", "date": 501}}]}‘;



echo "json str: $json_newstr\n";

$json = new Services_JSON(SERVICES_JSON_LOOSE_TYPE);

$start_object = $json->decode($json_newstr);

$result = jsonPath($start_object, "$..members..level");

echo "got all levels...\n";

var_dump($result);

$check_result_ok = true;

foreach($result as $level_to_check){

    if($level_to_check <= 0){

        $check_result_ok = false;

    }

}

var_dump($check_result_ok);

運行上述片段對json字符串進行檢查

task start...

json str: {"name": "name1", "date": 123, "members": [{"name":"alice", "level": 5, "lastpost": {"title": "noname1", "date": 500}} , {"name":"bob", "level": 0, "lastpost": {"title": "noname2", "date": 501}}]}

got all levels...

array(2) {

  [0]=>

  int(5)

  [1]=>

  int(0)

}

bool(false)

task end. run check for0 time(s)

我們輸出檢查結果為false,發現了一條不符合預期的數據。

4. 如何基於歷史特征進行統計?

  在以上部分我們了解如何提取特征後,我們可以考慮將其按照結構化保存在mysql裏。在前幾步執行過程中我們已經遍歷了json對應object的樹狀結構,以及通過xpath等樹狀描述對節點進行定位。將這幾個步驟組合起來即可:以接口為key,按需要保存若幹特征描述,對後續提取的結果進行檢查。
以上面的樣例json為例,我們可以保存每次抓取檢查時,/members[]數組下的元素個數,當和歷史平均值波動大於80%時認為是有問題報警。

5. 基於字符串的檢查

  除了上述的詳細檢查外,我們依然需要對字符串進行一些檢查,例如編碼、總長度和結構完整性。最後一點比較簡單,考慮調用lib時parse失敗,如果失敗說明json/xml結構不完整,保存現場供人工查看,調整case。

  以上就是關於數據接口檢查類的自動化測試思路,在設計上我們考慮到現在一些自動化監控/測試實踐中遇到的問題,和趨向最新機器學習思路提取特征的想法提出的想法。

檢查數據接口返回數據合法性