2017-02-10 88 views
0

單元測試以下redux異步操作的正確方法如何?如何使用redux對單元測試/存根異步調用

const client = contentful.createClient(clientConfig); 
 

 
export const fetchNavigation =() => { 
 
    return dispatch => { 
 

 
    return client.getEntries({content_type: 'navigation'}) 
 
     .then((entries) => { 
 
     console.log('All entries for content_type = navigation') 
 
     dispatch(receiveNavigation(entries)) 
 
     }) 
 
     .catch(error => { 
 
     \t console.log('Something went wrong'); 
 
     \t dispatch(fetchNavigationFailure(error)); 
 
     }); 
 
    } 
 
}

我不知道如何自定義通過client.getEntries執行的Web請求的響應體。我認爲用我自己的代替getEntries函數可以做到這一點。但是,我不知道從哪裏開始這樣做。

這裏是單元測試我寫的:

const middlewares = [ thunk ] 
 
const mockStore = configureMockStore(middlewares) 
 

 
describe('fetchNavigation',() => { 
 
    it('creates RECEIVE_NAVIGATION when fetching navigation is done',() => { 
 

 
    // Here I should prepare the client.getEntries() returned promise 
 

 
    const expectedBodyResponse = { includes: ['do something', 'yay!'] } 
 
    const expectedActions = [ 
 
     { type: actions.RECEIVE_NAVIGATION, navigation: expectedBodyResponse } 
 
    ] 
 
    const store = mockStore({ todos: [] }) 
 

 
    return store.dispatch(actions.fetchNavigation()) 
 
     .then(() => { 
 
     expect(store.getActions()).toEqual(expectedActions) 
 
     }) 
 
    }) 
 
})

回答

0

我用這種方式解決了。 首先,我使用函數initClient()和getClient()將客戶端的創建移動到了它自己的文件中。該模塊被稱爲contentfulClient。

然後,我發現它可以嘲笑實例化對象的功能興農:

import * as contentful from './services/contentfulClient'; 
 

 
const client = contentful.initClient(clientConfig); 
 
const navigation = { 
 
    items: ['page1', 'page2'] 
 
}; 
 

 
// Returns a promise with navigation as content 
 
sinon.stub(client, 'getEntries').resolves(navigation); 
 

 
// Assert 
 
return store.dispatch(actions.fetchNavigation()) 
 
     .then(() => {  \t expect(store.getActions()).toEqual(expectedActions) 
 
     })

0

IMO嘲諷getEntries(可能createClient)似乎是做正確的方式。 :) 這取決於你如何加載contentful sdk。正如我看到你正在使用ES模塊和茉莉花,對吧?

要模擬getEntries函數,您必須模擬createClient,因爲您的測試中無法訪問客戶端。

我覺得這個this answer可能是你要找的。

我剛剛寫下了一個例子。

import contentful from 'contentful'; 
 

 
export const fetchNavigation =() => { 
 
    return (dispatch) => { 
 
    return contentful.createClient({ accessToken: 'fooo', space: 'bar' }) 
 
     .getEntries({ content_type: 'navigation' }) 
 
     .then(() => { 
 
     dispatch('yeah'); 
 
     }) 
 
     .catch(error => console.error('Something went wrong', error)); 
 
    }; 
 
};

import { fetchNavigation } from '../Action'; 
 
import * as contentful from 'contentful'; 
 

 
describe('Contentful mocking',() => { 
 
    it('should be possible to mock Contentful', (done) => { 
 
    const client = { getEntries:() => { return Promise.resolve(); } }; 
 
    const spy = { 
 
     fn: (value) => { 
 
     expect(value).toBe('yeah'); 
 
     done(); 
 
     }, 
 
    }; 
 

 
    spyOn(contentful.default, 'createClient').and.returnValue(client); 
 

 
    fetchNavigation()(spy.fn); 
 
    }); 
 
});

我不得不搬到了createClient呼叫進入行動本身,否則我不認爲這是可能達到和嘲笑它時,它的隱藏在模塊範圍內。然後,我使用import * as contentful from 'contentful'來嘲笑和覆蓋所需的功能,並有靈活性來根據我的需要調整一切。

createClient的使用感覺有點不幸。我可能會重構一切,並會通過所有行爲的依賴關係client?這樣嘲笑會變得更容易,當你也有幾個動作模塊時,很可能不需要多次初始化客戶端?

+0

非常感謝您的回答,斯特凡。 – Alvaro