Thunk异步Action测试时请求头未Mock报错及优化方案问询
解决Nock测试Thunk异步Action时的请求头Mock问题
首先你的报错核心原因很明确:测试环境中authService.getAccessToken()返回了undefined,导致请求头x-access-token携带了非法值,触发了TypeError [ERR_HTTP_INVALID_HEADER_VALUE]错误。下面给你一套更简洁的测试方案和请求头Mock优化方法:
原代码回顾
发起异步请求的Thunk Action
buyerAction = (data = {}, cb) => (dispatch) => { console.log('x-access-token', authService.getAccessToken()); axios({ method: 'POST', url: `http://localhost:3001/api/manageUsers`, headers: { 'x-access-token': authService.getAccessToken() }, data }).then((res) => { if (res.status === 200 && res.data) { dispatch({ type: 'buyer_created', data: res.data.message }); if (data.role === 'buyer') { axios({ method: 'POST', url: `http://localhost:3001/api/populateBuyerLookUp`, headers: { 'x-access-token': authService.getAccessToken() }, data }).then((response) => { console.log(`${response} lookup is created`); }).catch((err) => { console.log(err); }); } cb(res.data.message); } }) .catch((err) => { console.log('_+_+_+', err); }); };
原始测试代码
const middleware = [thunk]; const mockStore = configureMockStore(middleware); describe('Async Actions', () => { const host = 'http://localhost'; axios.defaults.host = host; axios.defaults.adapter = httpAdapter; afterEach(() => { nock.cleanAll(); }); it('should create Async and load data', (done) => { nock('http://localhost:3001', { reqheaders: { 'x-access-token': 'authtoken' } }) .post('/api/manageUsers') .reply(200, { body: { name: 'prakash' } }); const store = mockStore({}, [{ type: 'buyer_created', data: 'message' }]); store.dispatch(buyerAction(undefined, () => {})); done(); }); });
优化后的解决方案
1. 先Mock认证服务的Token获取
测试时我们不需要依赖真实的认证状态,直接用Jest Mock掉authService.getAccessToken(),让它返回固定的测试用token:
// 放在测试文件顶部或describe块内部 jest.mock('../path/to/authService', () => ({ getAccessToken: jest.fn(() => 'authtoken') // 返回预设的测试token }));
2. 简化Thunk测试逻辑(弃用done回调)
用async/await替代done,配合mockStore的getActions()做断言,同时精简nock配置:
const middleware = [thunk]; const mockStore = configureMockStore(middleware); describe('Async Actions', () => { afterEach(() => { nock.cleanAll(); jest.clearAllMocks(); // 清除mock的调用记录,避免测试用例互相干扰 }); it('should dispatch buyer_created action on successful user creation', async () => { // 拦截第一个POST请求,验证请求头并返回模拟响应 const userCreateNock = nock('http://localhost:3001', { reqheaders: { 'x-access-token': 'authtoken' } }) .post('/api/manageUsers') .reply(200, { message: 'User created successfully' }); // 测试buyer角色时,拦截第二个lookup请求 const testData = { role: 'buyer' }; if (testData.role === 'buyer') { nock('http://localhost:3001', { reqheaders: { 'x-access-token': 'authtoken' } }) .post('/api/populateBuyerLookUp') .reply(200); } const store = mockStore({}); const mockCb = jest.fn(); // Mock回调函数,用来验证是否被调用 // 异步dispatch action,等待所有异步操作完成 await store.dispatch(buyerAction(testData, mockCb)); // 断言nock是否确实拦截到了请求 expect(userCreateNock.isDone()).toBe(true); // 断言正确的action被dispatch expect(store.getActions()).toEqual([ { type: 'buyer_created', data: 'User created successfully' } ]); // 断言回调函数被正确触发 expect(mockCb).toHaveBeenCalledWith('User created successfully'); }); });
3. 额外优化:统一请求头配置
为了避免在每个请求里重复写headers,可以给axios加个全局请求拦截器,自动注入token:
// 在axios初始化的文件中添加 axios.interceptors.request.use(config => { const token = authService.getAccessToken(); if (token) { config.headers['x-access-token'] = token; } return config; });
这样你的Thunk Action可以简化成:
buyerAction = (data = {}, cb) => (dispatch) => { axios({ method: 'POST', url: `http://localhost:3001/api/manageUsers`, data }).then((res) => { // 后续逻辑保持不变 }) .catch((err) => { console.log('_+_+_+', err); }); };
关键说明
- Mock认证服务:彻底解决测试环境中token为undefined的问题,确保请求头合法。
- 用async/await替代done:让测试代码更简洁易读,同时保证所有异步操作完成后再执行断言,避免异步时序问题。
- 验证nock调用状态:用
isDone()确保请求确实被拦截,避免测试用例"假通过"。
内容的提问来源于stack exchange,提问作者bikky barnwal
相关产品推荐
相关产品推荐

