Laravel一次單元測試發現的'BUG',分析並解決問題

下面這段代碼是Laravel自帶的表單驗證的語法,不太瞭解的能夠先查看文檔php

'group_num' => 'min:1|max:21'  // 也可使用between替換min和max

咱們指望的結果是能校驗group_num字段最小值是1,最大值是21laravel

but !!!數組

...單元測試

當我單元測試的時候發現,居然校驗經過了!測試

// 單元測試代碼

$warehouseId = 1;   
$prods = [      
    [         
        'prod_id' => 1,      
        'group_num' => 111,      
        'location' => 1,   
    ]
];   
$warehouseLogic = new WarehouseLogic();   
$result = $warehouseLogic->addProds($warehouseId, $prods);    
$this->assertEquals(ErrSvc::ERR_OK, $result['code']);

文檔分析

咱們看一下文檔ui

min:value

驗證中的字段必須具備最小值。字符串、數字、數組或是文件大小的計算方式都用 size 方法進行評估。this

文章提到數字使用size方法進行評估,咱們看一下size方法的文檔spa

size:value

驗證的字段必須具備與給定值匹配的大小。對於字符串來講,value 對應於字符數。對於數字來講,value 對應於給定的整數值。對於數組來講,
size 對應的是數組的 count 值。對文件來講,size 對應的是文件大小(單位 kb )。code

代碼分析

文檔一切正常,咱們翻一翻代碼試着分析下緣由blog

咱們找到驗證類的文件並打開:\vendor\laravel\framework\src\Illuminate\Validation\Validator.php

大約在1180行,咱們看到validateMin()方法

validateMin()

圖片描述

第一行代碼,是對參數個數進行驗證的,能夠pass掉

第二行代碼,調用了getSize()方法,並對getSize()返回結果直接進行大小比較,問題頗有可能就出如今getSize()方法身上

咱們看一下getSize()的代碼

getSize()

圖片描述

咱們能夠看到if裏面判斷了若是值是數字類型而且$hasNumeric就直接返回原始值,若是返回原始值的話,validateMin()方法應該會正確校驗,因此if條件應該是不成立

緣由極可能就在$hasNumeric這個變量上

$hasNumeric調用了$this->hasRule( $attribute, $this->numericRules)方法,並傳了兩個參數過去

$attribute:當前屬性名

$this->numericRules:['Numeric', 'Integer']

而後咱們看一下$this->hasRule()方法究竟作了什麼?

hasRule方法直接調用了$this->getRule()方法,而且將參數原封不動傳遞過去

咱們看一下getRule()方法幹了什麼?

getRule()

圖片描述

咱們已知$attribute是當前字段名,好比文章舉例用的字段group_num

$this->rules其實就是字段+校驗規則拼裝的數組,格式以下:

圖片描述

既然第一個if語句的兩個變量都知道了,咱們就能判斷出第一個條件是不成立的,咱們繼續看接下來的代碼

代碼是對當前須要校驗字段的規則進行遍歷,而且格式化

list($rule, $parameters) = $this->parseRule($rule);

假設是上圖中的group_num字段,他有3個校驗規則,分別是:required、min、max

第一次循環$rule就是Required,$parameters爲空

第二次循環$rule就是Min,$parameters就是[1]

咱們會發現parseRule($rule)後,會對規則進行in_array的判斷,$rules是參數(['Numeric', 'Integer'])上文有寫

假設咱們此時的字段是group_num:

第一個規則是required,條件不成立;

第二個規則是Min,條件依然不成立;

第三個規則是Max,條件仍是不成立!

getRule()返回值:

條件都不成立,方法走完,沒有任何返回值,返回值爲null

hasRule()返回值:

回到hasRule()方法,會對getRule()方法值進行is_null(),並進行邏輯非處理(!),因此返回值爲false

咱們接着回到getSize()方法,此時咱們就知道$hasNumeric的值是false

因此下面的if條件都不成立,最後Laravel使用mb_strlen()對咱們數字類型的值進行了長度計算!!!

解決問題

既然咱們知道緣由在於Laravel對當前字段全部規則進行了in_array($rule, ['Numeric', 'Integer'])

因此解決思路就是,若是咱們要對字段進行大小進行範圍校驗,咱們須要把規則修改爲:

'group_num' => 'integer|min:1|max:21'

因此文章開頭的校驗,對於數值類型的字段,是錯誤的!

其實這不是一個BUG,單純的是Laravel的校驗機制,不過Laravel文檔寫的很模糊!

因此你們在開發的時候記得必定要認真測試

原文在本身的博客:Laravel一次單元測試發現的’BUG’,分析並解決問題 - 木魚博客

相關文章
相關標籤/搜索