2017-02-10 83 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