在繼續進行說明之前,我們先來定義一下什么是可控測試。在這里我們對可控測試的定義如下。
待測試的promise對象
* 如果編寫預期為Fulfilled狀態的測試的話
* Rejected的時候要?**Fail**
* assertion 的結果不一致的時候要?**Fail**
* 如果預期為Rejected狀態的話
* 結果為Fulfilled 測試為?**Fail**
* assertion 的結果不一致的時候要?**Fail**
如果一個測試能網羅上面的用例(Fail)項,那么我們就稱其為可控測試。
也就是說,一個測試用例應該包括下面的測試內容。
* 結果滿足 Fulfilled or Rejected 之一
* 對傳遞給assertion的值進行檢查
在前面使用了?`.then`?的代碼就是一個期望結果為 Rejected 的測試。
~~~
promise.then(failTest, function(error){
// 通過assert驗證error對象
assert(error instanceof Error);
});
~~~
## 3.3.1\. 必須明確指定轉換后的狀態
為了編寫有效的測試代碼, 我們需要明確指定?[promise的狀態](http://liubin.github.io/promises-book/#promise-states)?為 Fulfilled or Rejected 的兩者之一。
但是由于?`.then`?的話在調用的時候可以省略參數,有時候可能會忘記加入使測試失敗的條件。
因此,我們可以定義一個helper函數,用來明確定義promise期望的狀態。
> 筆者(原著者)創建了一個類庫?[azu/promise-test-helper](https://github.com/azu/promise-test-helper)?以方便對Promise進行測試,本文中使用的是這個類庫的簡略版。
首先我們創建一個名為?`shouldRejected`?的helper函數,用來在剛才的?`.then`?的例子中,期待測試返回狀態為 onRejected 的結果的例子。
shouldRejected-test.js
~~~
var assert = require('power-assert');
function shouldRejected(promise) {
return {
'catch': function (fn) {
return promise.then(function () {
throw new Error('Expected promise to be rejected but it was fulfilled');
}, function (reason) {
fn.call(promise, reason);
});
}
};
}
it('should be rejected', function () {
var promise = Promise.reject(new Error('human error'));
return shouldRejected(promise).catch(function (error) {
assert(error.message === 'human error');
});
});
~~~
`shouldRejected`?函數接收一個promise對象作為參數,并且返回一個帶有?`catch`?方法的對象。
在這個?`catch`?中可以使用和 onRejected 里一樣的代碼,因此我們可以在?`catch`?使用基于 assertion 方法的測試代碼。
在?`shouldRejected`?外部,都是類似如下、和普通的promise處理大同小異的代碼。
1. 將需要測試的promise對象傳遞給?`shouldRejected`?方法
2. 在返回的對象的?`catch`?方法中編寫進行onRejected處理的代碼
3. 在onRejected里使用assertion進行判斷
在使用?`shouldRejected`?函數的時候,如果是 Fulfilled 被調用了的話,則會throw一個異常,測試也會失敗。
~~~
promise.then(failTest, function(error){
assert(error.message === 'human error');
});
// == 幾乎這兩段代碼是同樣的意思
shouldRejected(promise).catch(function (error) {
assert(error.message === 'human error');
});
~~~
使用?`shouldRejected`?這樣的helper函數,測試代碼也會變得很直觀。

Figure 10\. Promise onRejected test
像上面一樣,我們也可以編寫一個測試promise對象期待結果為Fulfilled的?`shouldFulfilled`?helper函數。
shouldFulfilled-test.js
~~~
var assert = require('power-assert');
function shouldFulfilled(promise) {
return {
'then': function (fn) {
return promise.then(function (value) {
fn.call(promise, value);
}, function (reason) {
throw reason;
});
}
};
}
it('should be fulfilled', function () {
var promise = Promise.resolve('value');
return shouldFulfilled(promise).then(function (value) {
assert(value === 'value');
});
});
~~~
這和上面的?[shouldRejected-test.js](http://liubin.github.io/promises-book/#shouldRejected-test.js)?結構基本相同,只不過返回對象的?`catch`?方法變為了?`then`?,promise.then的兩個參數也調換了。
## 3.3.2\. 小結
在本小節我們學習了如何編寫針對Promise特定狀態的測試代碼,以及如何使用便于測試的helper函數。
> 這里我們使用到的?`shouldFulfilled`?和?`shouldRejected`?也可以在下面的類庫中找到。
> [azu/promise-test-helper](https://github.com/azu/promise-test-helper)。
此外,本小節中的helper方法都是以?[Mocha對Promise的支持](http://liubin.github.io/promises-book/#mocha-promise)?為前提的, 在?[基于`done` 的測試](http://liubin.github.io/promises-book/#done-promise-test)?中使用的話可能會比較麻煩。
是使用基于測試框架對Promis的支持,還是使用基于類似`done`?這樣回調風格的測試方式,每個人都可以自由的選擇,只是風格問題,我覺得倒沒必要去爭一個孰優孰劣。
比如在?[CoffeeScript](http://coffeescript.org/)下進行測試的話,由于CoffeeScript 會隱式的使用return返回,所以使用?`done`?的話可能更容易理解一些。
對Promise進行測試比對通常的異步函數進行測試坑更多,雖說采取什么樣的測試方法是個人的自由,但是在同一項目中采取前后風格一致的測試則是非常重要。
- 前言
- 第一章 - 什么是Promise
- 1.1. 什么是Promise
- 1.2. Promise簡介
- 1.3. 編寫Promise代碼
- 第二章 - 實戰Promise
- 2.1. Promise.resolve
- 2.2. Promise.reject
- 2.3. 專欄: Promise只能進行異步操作?
- 2.4. Promise#then
- 2.5. Promise#catch
- 2.6. 專欄: 每次調用then都會返回一個新創建的promise對象
- 2.7. Promise和數組
- 2.8. Promise.all
- 2.9. Promise.race
- 2.10. then or catch?
- 第三章 - Promise測試
- 3.1. 基本測試
- 3.2. Mocha對Promise的支持
- 3.3. 編寫可控測試(controllable tests)
- 第四章 - Advanced
- 4.1. Promise的實現類庫(Library)
- 4.2. Promise.resolve和Thenable
- 4.3. 使用reject而不是throw
- 4.4. Deferred和Promise
- 4.5. 使用Promise.race和delay取消XHR請求
- 4.6. 什么是 Promise.prototype.done ?
- 4.7. Promise和方法鏈(method chain)
- 4.8. 使用Promise進行順序(sequence)處理
- 第五章 - Promises API Reference
- 5.1. Promise#then
- 5.2. Promise#catch
- 5.3. Promise.resolve
- 5.4. Promise.reject
- 5.5. Promise.all
- 5.6. Promise.race
- 第六章 - 用語集
- 第七章 - 參考網站
- 第八章 - 關于作者
- 第九章 - 關于譯者