2017-05-25 181 views
0

我曾經有過下面的代碼中承諾:測試承諾

function makeCall(userInfo) { 
    api.postUser(userInfo).then(response => { 
    utils.redirect(response.url); 
    }) 

    // other logic 
    return somethingElse; 
} 

,我能夠寫一個測試,是這樣的:

const successPromise = Promise.resolve({ url: 'successUrl' }) 

beforeEach(function() { 
    sinon.stub(api.postUser).returns(successPromise); 
} 

afterEach(function() { 
    api.postUser.restore(); 
} 

it "calls API properly and redirects" do 
    makeCall({}); 
    expect(api.postUser).calledWith(userInfo).toBe(true); 
    successPromise.then(() => { 
    expect(utils.redirect.calledWith('successUrl')).toBe(true); 
    done(); 
    } 
emd 

,一切都是綠色的。現在

,我不得不添加另一個承諾,使另外一個外部調用,這樣做的API postUser調用之前,所以我的代碼如下所示:

function makeCall(names) { 
    fetchUserData(names).then(userData => { 
    return api.postUser(userData).then(response => { 
    utils.redirect(response.url); 
    }) 
    }) 

    // other logic 
    return somethingElse; 
} 

其中fetchUseData是許多承諾,比如像鏈:

function fetchNames(names) { 
    // some name regions 
    return Promise.all(names); 
} 
function fetchUserData(names) { 
    fetchUsersByNames(names).then(users => { 
    // For now we just choose first user 
    { 
     id: users[0].id, 
     name: users[0].name, 
    } 
    }); 
} 

而且我的測試失敗了。我正在嘗試瞭解如何更改我的測試,以確保我仍在測試是否正確執行了最終的API調用,並且重定向也已完成。我想存根fetchUserData(names),以防止做這個HTTP調用。

+0

您沒有使用正確的承諾。你的代碼沒有一個'return'語句,它應該有幾個(或者至少應該使用箭頭函數,這樣你不需要它們,你不這樣做)。您不斷將自由浮動的代碼塊稱爲「我的代碼」。什麼是調用該代碼?你的測試不應該調用它嗎?在您的代碼已經運行到其他地方後,它們似乎正在觀察一些結果。 – JLRishe

+0

嘗試返回內部承諾(添加'return')以創建承諾鏈。 – TiagoLr

+0

@JLRishe更新。我主要關心的是我應該把「done()」放在哪裏,或者我應該做什麼,這樣承諾就會被扼殺和評估。 –

回答

0

您沒有使用正確的承諾。你的代碼沒有單一的return陳述,當它應該有幾個(或者它應該使用箭頭功能,你不需要它們,你不這樣做)。

修復代碼:

function makeCall(names) { 
    // v---- return 
    return fetchUserData(names).then(userData => { 
    // v---- return 
    return api.postUser(userData).then(response => { 
     utils.redirect(response.url); 
    }) 
    }) 
} 


function fetchUserData(names) { 
    // v---- return 
    return fetchUsersByNames(names).then(users => { 
    // For now we just choose first user 
    // v---- return 
    return { 
     id: users[0].id, 
     name: users[0].name, 
    } 
    }); 
} 

一旦你做到了這一點,你可以有你的測試等待所有操作完成。

測試代碼:

makeCall(['name']).then(() => 
    expect(api.postUser).calledWith(userInfo).toBe(true); 
    expect(utils.redirect.calledWith('successUrl')).toBe(true); 
    done(); 
}); 
+0

嘿JLRishe,如果我不想在最外層的函數中返回承諾呢?這是一個副作用,我想返回其他的東西(原因在於這是在redux中間件中使用的,而且你總是需要返回'next(action)'。我編輯了我的問題來解釋爲什麼我沒有最後承諾的回報,而是把它當作函數的副作用 –

+0

@HommerSmith這聽起來像你可能試圖混合兩個不兼容的範例,看起來像redux中間件有它自己的處理方式異步與promise不同,遺憾的是,我對redux中間件一無所知,但[這裏有一些關於使用promise的信息](https://www.google.co.jp/search?q=redux+中間件+ promises&oq = redux +中間件+ promises&aqs = chrome..69i64.4271j0j4&sourceid = chrome&ie = UTF-8) – JLRishe

+0

感謝JLRishe,因爲我的測試通過了,只要我有一個承諾(即使它沒有返回)我認爲,當我在最初的p中增加更多的承諾時romise,我可以弄明白。 –

0

你應該加一個return語句,否則你是不是無處返回承諾:

function fetchNames(names) { 
    // some name regions 
    return Promise.all(names); 
} 
function fetchUserData(names) { 
    return fetchUsersByNames(names).then(users => { 
    // For now we just choose first user 
    { 
     id: users[0].id, 
     name: users[0].name, 
    } 
    }); 
} 

所以,當你使用Promise.all(),那麼你將有作爲的承諾與所有的數組的結果所有承諾返回的值。 所以後來這個方法看起來應該是這樣叫的時候:

fetchNames(names).then((arrayOfResolvedPromises) => { 
// here you will have all your promised resolved and the array holds all the results 
}); 

所以你的測試,你可以將你的裏面塊,其中所有的承諾將得到解決內部完成。

此外,我強烈建議您使用庫作爲測試承諾的承諾。 它有很多很好的方法來測試你的承諾。

https://github.com/domenic/chai-as-promised

+0

這不會讓我的測試通過。如果我有幾個承諾,我應該在哪裏放置'done()'? –

+0

你需要在Promise.all()中保留所有的承諾。在其當前塊內,你確定所有的承諾已經解決 – quirimmo