一. 簡介
斷言是程式設計術語,表示為一些布林表示式,程式設計師相信在程式中的某個特定點該表示式值為真,可以在任何時候啟用和禁用斷言驗證,因此可以在測試時啟用斷言而在部署時禁用斷言。同樣,程式投入執行後,終端使用者在遇到問題時可以重新啟用斷言。
使用斷言可以建立更穩定、品質更好且 不易於出錯的程式碼。當需要在一個值為FALSE時中斷當前操作的話,可以使用斷言。【單元測試】必須使用斷言。
Node提供了 10 多個斷言測試的函式,用於測試不變式,我在文章中將這 10 多個函式進行了分組,方便理解記憶。
【提示】在本文章中,expected 代表預期值,actual 代表實際值, message 代表自定義資訊
二. 判斷值是否為真值
判斷值是否為真值有以下兩個斷言測試函式
2.1 assert(value[, message])
這個測試函式在 【Boolean(value)】 為 【true】時通過斷言測試,否則丟擲 【AssertionError】
const assert = require("assert");
assert("blue","第一個值為false時以我為錯誤資訊丟擲");
assert(true,"第一個值為false時以我為錯誤資訊丟擲");
上面一段程式碼由於【Boolean(value)】全部為 true,所以全部通過斷言測試
assert(false,"第一個值為false時以我為錯誤資訊丟擲");
// AssertionError [ERR_ASSERTION]: 第一個值為false時以我為錯誤資訊丟擲
上面程式碼中 value 為false,則丟擲一個帶有 message 屬性的 【AssertionError】,其中 message 屬性的值等於傳入的 message 引數的值。 【如果 message 引數為 undefined,則賦予預設的錯誤資訊】。
assert(false);
// AssertionError [ERR_ASSERTION]: false == true
上面程式碼由於沒有指定【message】引數,丟擲的為預設錯誤資訊的【AssertionError】
2.2 assert.ok(value[, message])
assert.ok() 與 assert()的作用是一樣的,都是測試【value】是否為真值。而且用法也一樣,所以可以將assert()視為assert.ok()的語法糖
const assert = require("assert");
assert.ok(true);
assert.ok(1);
上面程式碼【Boolean(value)】全部為 true,所以全部斷言通過,下面是斷言不通過的情況,分別列出了預設錯誤資訊
assert.ok(0);
//AssertionError [ERR_ASSERTION]: 0 == true
assert.ok(false);
//AssertionError [ERR_ASSERTION]: false == true
assert.ok(false,"自定義錯誤資訊");
//AssertionError [ERR_ASSERTION]: 自定義錯誤資訊
三. 判斷預期值和實際值相等(==)
這一組裡面有兩個測試函式,用於測試預期值與實際值是否相等,如果相等則斷言通過,否則丟擲 【AssertionError】
3.1 assert.equal(actual, expected[, message])
assert.equal()用於測試期望值和實際值是否相等,【在值型別的時候比較的是兩個值是否相等,當預期值和實際值為引用型別的時候,比較的是值得引用】
assert.equal(1, 1);
assert.equal("1", 1);
上面程式碼是對值型別進行的比較,說明equal()在內部使用的是(==),而非嚴格相等,待會兒我會總結到嚴格相等(===)
assert.equal({},{},"AssertionError");
assert.equal(() => { }, () => { }, "AssertionError");
assert.equal([],[],`AssertionError`);
上面三個表示式都會丟擲【message】屬性值為`AssertionError`的【AssertionError】物件,【所以當值為引用型別的時候,equal()比較的是值得引用,因此兩個引用型別的值是沒法通過equal()斷言的】
const obj={};
assert.equal(obj,obj);
// 斷言通過
上面程式碼由於比較的是同一個物件,兩個值得引用相等,所以斷言通過。
3.2 assert.deepEqual(actual, expected[, message])
同樣也是測試 預期值 和 實際值 是否相等,使用的仍然是(==),但是與equal不同的是,【deepEqual()在對引用型別進行比較的時候,不是對值的引用進行比較,而是比較的物件的屬性值】
const a = `Blue`, b = `Pink`;
assert.deepEqual(a,a,`actual unequal to expected`);
// 斷言通過
assert.deepEqual(a,b,`actual unequal to expected`);
// AssertionError [ERR_ASSERTION]: actual unequal to expected
上面是對值型別進行的比較,和equal()沒有任何的區別
const obj1 = { name: "foo", gender: "men" },
obj2 = { name: "foo", gender: "men" },
obj3 = { name: "bar", gender: "men" }
assert.deepEqual(obj1, obj2, `actual unequal to expected`);
// 斷言通過
assert.deepEqual(obj1, obj3, `actual unequal to expected`);
// AssertionError [ERR_ASSERTION]: actual unequal to expected
上面程式碼是對引用型別的比較,可以看出【deepEqual()】比較的是屬性值,而非引用,這是與equal()不同的地方。
【注意!!】deepEqual()只測試可列舉的自身屬性,不測試物件的原型、連線符、或不可列舉的屬性(這些情況使用 assert.deepStrictEqual(),稍後會總結到)
const son1 = Object.create(obj1),
son2 = Object.create(obj2);
son1.name="Summer";
son2.name="Summer";
assert.deepEqual(son1,son2,"actual unequal to expected");
// 斷言通過
上面程式碼中 son1 和 son2 分別繼承與兩個不同的物件,都擁有 name 為 “Summer” 的屬性,最後的的結果是通過,說明【deepEqual()不測試物件的原型】
const ena = {}, enb = {};
Object.defineProperties(ena,{
name:{
value:"Blue"
},
hobby:{
value:"foo",
enumerable:false //可列舉性設定為false
}
});
Object.defineProperties(enb,{
name:{
value:"Blue"
},
hobby:{
value:"bar",
enumerable:false //可列舉性設定為false
}
})
assert.deepEqual(ena,enb,"actual unequal to expected")
//ok,actual equal to expected
上面程式碼中 ena 和 enb 用於相同的可列舉屬性【name】,有著值不同的不可列舉屬性【hobby】,說明【deepEqual()不測試物件的不可列舉的屬性】
四. 判斷預期值和實際值全等(===)
這組測試函式是用於判斷預期值和實際值是否深度相等的,內部使用的是(===),所以物件的原型也會進行比較,值得型別也是比較的範圍。這組也有兩個測試函式。
4.1 assert.deepStrictEqual(actual, expected[, message])
由於內部使用的是全等(===),所以物件的原型也會計入比較的範圍
const obj1 = { name: "foo", gender: "men" },
obj2 = { name: "bar", gender: "men" }
const son1 = Object.create(obj1),
son2 = Object.create(obj2);
son1.name = "Summer";
son2.name = "Summer";
assert.deepEqual(son1, son2, "actual unequal to expected");
//斷言通過
assert.deepStrictEqual(son1, son2, "actual unequal to expected")
//AssertionError [ERR_ASSERTION]: actual unequal to expected
上面程式碼使用了deepEqual()和deepStrictEqual()進行了斷言測試,son1 和 son2 分別繼承與兩個不同的物件,但是擁有相同的屬性值。可以看出【deepEqual()是不會考慮物件的原型的,deepStrictEqual()將原型物件列入了比較物件】
4.2 assert.strictEqual(actual, expected[, message])
strictEqual()是equal()的加強,考慮了資料型別;如果actual === expected,則斷言通過,否則丟擲AssertionError,message?message:預設錯誤資訊。
assert.strictEqual(1, 2);
// 丟擲 AssertionError: 1 === 2
assert.strictEqual(1, 1);
// 測試通過。
assert.strictEqual(1, `1`);
// 丟擲 AssertionError: 1 === `1`
assert.equal(1, `1`);
// 測試通過。
【提示!!】對引用型別還是永遠通不過【strictEqual()】斷言測試
五. 判斷預期值和實際值不相等(!=)
上面總結到了判斷預期值和實際值相等,這兒總結一下判斷預期值和實際值不想等的兩個測試函式,實際上就是上面 (三) 的逆運算。
5.1 assert.notEqual(actual, expected[, message])
【notEqual()】為 【equal()】的逆運算,如果 actual!= expected 則斷言通過,同樣對於值型別是單純對值進行比較,對應引用型別比較的是值得引用
assert.notEqual("1", "2");
// 斷言通過
assert.notEqual("1", 2);
// 斷言通過
assert.notEqual("1", 1);
// AssertionError [ERR_ASSERTION]: `1` != 1
上面程式碼是對值型別進行的比較,第三個表示式的預設資訊可以看出內部使用的是(!=)
assert.notEqual({ a: "foo" }, { a: "foo" });
assert.notEqual(() => { }, () => { });
assert.notEqual([], []);
上面的程式碼是對引用型別進行的斷言測試,【notEqual()】對於兩個物件的測試通過是一個【恆成立】的結果。
5.2 assert.notDeepEqual(actual, expected[, message])
【notDeepEqual()】為 【deepEqual()】的逆運算,如果 actual!= expected 則斷言通過,不同於notEqual()的是對於引用型別是對值進行判斷,不比對原型、不可列舉屬性,只比對自有可列舉屬性,斷言通過。
const obj1 = { a: "foo" },
obj2 = { b: "bar" },
obj3 = Object.create(obj1);
assert.notDeepEqual(obj1,obj1,`actual equal to expected`);
// AssertionError [ERR_ASSERTION]: actual equal to expected
assert.notDeepEqual(obj1,obj2,`actual equal to expected`);
// 斷言通過
assert.notDeepEqual(obj1,obj3,`actual equal to expected`);
// 斷言通過
上面程式碼中最後一個表示式斷言通過,說明【不比對原型、不可列舉屬性,只比對自有可列舉屬性】
【注意!!】與notEqual的區別,也就是deepEqual和equal的區別,在引用資料型別的時候,deepEqual是比較的值而非引用,equal對比的是引用,所以引用型別在equal的時候是永遠無法通過斷言測試的,以此類推,引用型別在notEqual時是永遠否可以通過斷言測試的。
六. 判斷預期值和實際值嚴格不相等(!==)
上面總結到了判斷預期值和實際值嚴格相等,這兒總結一下判斷預期值和實際值嚴格不相等的兩個測試函式,實際上就是上面 (四) 的逆運算。
6.1 assert.notStrictEqual(actual, expected[, message])
如果actual與expected不 !== 則斷言通過, 與 assert.deepStrictEqual() 相反
assert.notStrictEqual("1", 1);
// 斷言通過
assert.notStrictEqual("1", "1");
// AssertionError [ERR_ASSERTION]: `1` !== `1`
上面程式碼是對值型別進行的斷言測試,可以看出【notStrictEqual()】考慮了資料型別
assert.notStrictEqual({ a: "foo" }, { a: "foo" });
assert.notStrictEqual(() => { }, () => { });
assert.notStrictEqual([], []);
上面程式碼是對引用型別的測試,全部通過,以上表示式是恆通過的。
6.2 assert.notDeepStrictEqual(actual, expected[, message])
notDeepStrictEqual()就是deepStrictEqual()的逆運算,如果 actual !== expected 則斷言通過,否則丟擲AssertionError。
assert.notDeepStrictEqual({ a: `1` }, { a: 1 });
//斷言通過
assert.notDeepStrictEqual({ a: `1` }, { a: "1" });
//AssertionError [ERR_ASSERTION]: { a: `1` } notDeepStrictEqual { a: `1` }
七. 斷言錯誤並丟擲
這一組有 四 個(可以說是 三 個)測試函式,是對錯誤進行的處理。
7.1 assert.fail(message)
這個測試函式不多說,可以看錯是下一個函式的過載,用於主動丟擲帶有【message】屬性的【AssertionError】物件
assert.fail("自定義錯誤資訊");
// AssertionError [ERR_ASSERTION]: 自定義錯誤資訊
7.2 assert.fail(actual, expected[, message[, operator[, stackStartFunction]]])
該測試函式用於主動丟擲自定義錯誤資訊,丟擲錯誤資訊格式:【actual 引數 + operator 引數 + expected 引數】
assert.fail("BLUE","PINK");
// AssertionError [ERR_ASSERTION]: `BLUE` != `PINK`
上面程式碼不提供【message】和【operator】,則【operator】預設為 【!=】
assert.fail("BLUE","PINK","自定義的錯誤資訊");
// AssertionError [ERR_ASSERTION]: 自定義的錯誤資訊
assert.fail("BLUE","PINK","自定義的錯誤資訊","?",()=>{
console.log("hello");
});
// AssertionError [ERR_ASSERTION]: 自定義的錯誤資訊
上面程式碼提供【message】,這時候 【actual】、【operator】、【expected】等引數會被列入錯誤物件屬性中
assert.fail("BLUE","PINK",undefined);
// AssertionError [ERR_ASSERTION]: `BLUE` undefined `PINK`
assert.fail("BLUE","PINK",undefined,"?");
// AssertionError [ERR_ASSERTION]: `BLUE` ? `PINK`
上面程式碼是【message】為 undefined 時,會檢測【operator】引數,【operator?operator:undefined 】
7.3 assert.throws(block,error, message)
引數說明:
- block <Function>
- error <RegExp> | <Function>
- message <any>
【說明!!】如果block丟擲的錯誤滿足error引數,也就是丟擲錯誤與期望一致,則斷言通過,否則丟擲block中的錯誤,如果block不丟擲錯誤,則丟擲【AssertionError 】。
【提示!!】error 引數可以是建構函式、正規表示式、或自定義函式。
assert.throws(
() => {
throw new Error(`錯誤資訊`);
},
Error
);
上面程式碼中 error 引數為建構函式,【block】丟擲的錯誤與預期的一致,所以斷言通過。
assert.throws(
() => {
throw new Error(`錯誤資訊`);
},
/錯誤/
);
上面程式碼中 error 引數為正規表示式,【block】丟擲的錯誤滿足正規表示式,所以斷言通過。
【注意!!】error 引數不能是字串。 如果第二個引數是字串,則視為省略 error 引數,傳入的字串會被用於 【message】 引數,
// 這是錯誤的!不要這麼做!
assert.throws(myFunction, `錯誤資訊`, `沒有丟擲期望的資訊`);
// 應該這麼做。
assert.throws(myFunction, /錯誤資訊/, `沒有丟擲期望的資訊`);
下面程式碼,【error】 引數為自定義函式
assert.throws(
() => {
throw new Error(`錯誤資訊`);
},
function (err) {
if ((err instanceof Error) && /錯誤/.test(err)) {
return true;
}
},
`不是期望的錯誤`
);
7.4 assert.doesNotThrow(block, error, message)
【說明!!】預期的錯誤和實際的錯誤一致時,不丟擲實際錯誤,丟擲AssertionError,不一致則丟擲實際錯誤資訊
assert.doesNotThrow(
() => {
throw new TypeError(`錯誤資訊`);
},
SyntaxError
);
以上例子會丟擲 TypeError,因為在斷言中沒有匹配的錯誤型別
assert.doesNotThrow(
() => {
throw new TypeError(`錯誤資訊`);
},
TypeError
);
以上例子會丟擲一個帶有 Got unwanted exception (TypeError).. 資訊的 AssertionError
assert.doesNotThrow(
() => {
throw new TypeError(`錯誤資訊`);
},
TypeError,
`丟擲錯誤`
);
// 丟擲 AssertionError: Got unwanted exception (TypeError). 丟擲錯誤
上面程式碼說明:如果丟擲了 AssertionError 且有給 message 引數傳值,則 message 引數的值會被附加到 AssertionError 的資訊中
八. 判斷值是否為真
這兒只有一個測試函式了
8.1 assert.ifError(value)
如果value的值為真或者可以轉換成true,則丟擲value,否則斷言通過。
assert.ifError(true);
//丟擲true
assert.ifError(false);
//斷言通過
上面程式碼中是直接給出的 布林 型別的值,如果值為 true 則會將該值丟擲,否則什麼也不做
assert.ifError(0);
//斷言通過
assert.ifError("0");
//丟擲 "0"
assert.ifError(1);
//丟擲 1
assert.ifError(new Error());
//丟擲 Error,物件名稱
上面程式碼中全部是通過 Boolean(value) 轉換之後再進行的測試,利用這個特性我們可以將此測試函式用於測試回撥函式的 error 引數。
————————–END————————–
CSDN【Node斷言assert】同步更新