亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

開玩笑地忽略被拒絕的“一勞永逸”承諾

開玩笑地忽略被拒絕的“一勞永逸”承諾

尚方寶劍之說 2023-10-14 11:12:16
我的商店的processAction()函數以“即發即忘”的方式調用私有異步函數,然后進行提取。 processAction()它本身不處理任何錯誤處理,并且在瀏覽器中,如果獲取失敗,外部庫將處理所有未捕獲的承諾拒絕。因此,如果我模擬我的獲取以拒絕,則私有函數(我正在測試的效果)將拒絕。由于我沒有對異步函數調用創建的承諾的引用,因此我無法在測試中捕獲拒絕,但測試失敗,因為存在未處理的拒絕。我怎樣才能告訴 jest 接受這種調用私有函數本身的短時間,而不是僅僅觸發調用它的操作?動作.tsconst actions = {  doTheThing() {    dispatch({ type: 'DO_THE_THING' });  },};export default actions;商店.tsimport fetch from './fetch';class Store {  isFetching = false;  // ...  processAction({ type, payload }: { type: string, payload: any }) {    switch (type) {      case 'DO_THE_THING':        this.fetchTheThing();        break;    }  }  private async fetchTheThing() {    try {      this.isFetching = true;      const result = await fetch(myUrl);      // ...    } finally {      this.isFetching = false;    }  }}export default new Store();__mocks__/fetch.tslet val: any;interface fetch {  __setVal(value: any): void;}export default async function fetch() {  return val;}fetch.__setVal = function(value: any) {  val = value;};商店.test.tsimport actions from './actions';import store from './store';const fetch = (require('./fetch') as import('./__mocks__/fetch')).default;jest.mock('./fetch');test('it sets/unsets isFetching on failure', async () => {  let rej: () => void;  fetch.__setVal(new Promise((_, reject) => rej = reject));  expect(store.isFetching).toBe(false);  Actions.doTheThing();  await Promise.sleep(); // helper function  expect(store.isFetching).toBe(true);  rej(); // <---- test fails here  await Promise.sleep();  expect(store.isFetching).toBe(false);});
查看完整描述

2 回答

?
ABOUTYOU

TA貢獻1812條經驗 獲得超5個贊

我的函數以“即發即忘”的方式調用私有異步函數,并且不添加任何錯誤處理。

不要那樣做。

外部庫處理所有未捕獲的承諾拒絕。在生產中,我希望 shell 來處理它,所以我不想在函數本身中處理它。

不要依賴這個外部庫。

您應該在函數中使用自己的全局錯誤處理函數。

在生產中,讓該錯誤處理函數簡單地重新拋出異常,以便環境能夠捕獲該異常,或者更好的是,如果可能的話,直接調用 shell 錯誤處理函數。

在測試中,您可以模擬自己的全局處理程序,并斷言它是使用預期參數調用的。


查看完整回答
反對 回復 2023-10-14
?
qq_花開花謝_0

TA貢獻1835條經驗 獲得超7個贊

processAction是同步的并且不知道 Promise,這會導致懸空的 Promise。懸空承諾永遠不應該拒絕,因為這會導致未經處理的拒絕,這是一種例外。根據環境的不同,這可能會導致應用程序崩潰。即使異常是全局處理的,這也不應該成為不處理預期錯誤的理由。


正確的方法是在fetchTheThing拒絕發生的地方明確抑制拒絕:


  private async fetchTheThing() {

    try {

      ... 

    } catch {} finally {

      this.isFetching = false;

    }

  }

或者在這種情況下,它更像processAction是導致懸而未決的承諾:


this.fetchTheThing().catch(() => {});

否則將調度未處理的拒絕事件。


如果沒有的話,可以通過監聽事件來測試:


  ...

  let onRej = jest.fn();

  process.once('unhandledRejection', onRej);

  rej();

  await Promise.sleep();

  expect(onRej).toBeCalled();

  expect(store.isFetching).toBe(false);

如果已經有另一個監聽器,這將無法按預期工作unhandledRejection,這在良好的 Jest 設置中是可以預期的。如果是這種情況,唯一不會影響其他測試的解決方法是在測試之前重置它們并在測試之后重新添加:


let listeners;


beforeEach(() => {

  listeners = process.rawListeners('unhandledRejection');

  process.removeAllListeners('unhandledRejection');

});


afterEach(() => {

  (typeof listeners === 'function' ? [listeners] : listeners).forEach(listener => {

    process.on('unhandledRejection', listener);

  });

})

不建議這樣做,使用時應自行承擔風險,因為這表明錯誤處理存在更深層次的問題,而在正確設計的 JavaScript 應用程序中通常不可接受。


查看完整回答
反對 回復 2023-10-14
  • 2 回答
  • 0 關注
  • 147 瀏覽
慕課專欄
更多

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號