使用axios-mock-adapter模拟Spotify Post API请求失败排查
使用axios-mock-adapter模拟Spotify令牌请求的问题排查与解决
问题场景
我在尝试用axios-mock-adapter模拟Spotify API的令牌获取请求时遇到了两个问题:
- 初始测试中,mock的POST请求未被触发,
tokenResponse始终为undefined,调试显示mock已正确配置、请求参数匹配,但问题依旧。 - 后续为
AuthManager创建了统一的axios实例,确保业务代码和测试代码使用同一实例,但mock仍不生效,反而触发了真实的API请求。
业务代码片段:
export const discovery = { authorizationEndpoint: "https://accounts.spotify.com/authorize", tokenEndpoint: "https://accounts.spotify.com/api/token", }; export const getTokenRequest = async (response: AuthSessionResult, request: AuthRequest) => { const accessCode = response.params.code; console.log("Successfully authorised user and obtained access code", accessCode); await AsyncStorage.setItem("code", accessCode); const codeVerifier = request?.codeVerifier || "" console.log("Successfully obtained code verifier from request", codeVerifier); await AsyncStorage.setItem("codeVerifier", codeVerifier); const data = { client_id: clientId, grant_type: "authorization_code", code: accessCode, redirect_uri: redirectUri, code_verifier: codeVerifier } try { const tokenResponse = await axios.post( discovery.tokenEndpoint, qs.stringify(data), { headers: { "Content-Type": "application/x-www-form-urlencoded", } }) console.log("Successfully obtained token response", tokenResponse); storeAccessToken(tokenResponse); storeRefreshToken(tokenResponse); } catch(error) { console.log("Failed to obtain token request", error) } }
测试代码片段:
it("obtains an access code and a refresh token from the authorization code", async () => { const mockAxiosAdapter = new MockAdapter(axios); const mockTokenResponse = { data: { access_token: "DEF", refresh_token: "ABC" } }; const authResult = { type: "success", params: { code: "123" } } const authRequest = { codeVerifier: "XYZ" } mockAxiosAdapter.onPost(AuthManager.discovery.tokenEndpoint).reply(200, mockTokenResponse); await AuthManager.getTokenRequest(authResult as AuthSessionResult, authRequest as AuthRequest); expect(AsyncStorage.setItem).toHaveBeenCalledWith("access_token", "DEF"); expect(AsyncStorage.setItem).toHaveBeenCalledWith("refresh_token", "ABC"); })
问题根源分析
- axios实例不一致:业务代码若使用全局axios,测试却mock自定义实例,或反之,都会导致mock不生效;统一实例后仍触发真实请求,可能是实例引用错误或mock配置时机不对。
- 请求匹配严格性:
axios-mock-adapter对请求的URL、头信息、请求体格式是严格匹配的,任何细节不匹配都会导致mock不触发。
具体修复方案
1. 强制业务与测试使用同一axios实例
在AuthManager中创建并导出专属axios实例,业务代码和测试代码都使用这个实例:
// AuthManager.js import axios from 'axios'; // 创建统一实例并导出 export const axiosInstance = axios.create(); export const discovery = { authorizationEndpoint: "https://accounts.spotify.com/authorize", tokenEndpoint: "https://accounts.spotify.com/api/token", }; // 业务代码中使用该实例发送请求 export const getTokenRequest = async (response: AuthSessionResult, request: AuthRequest) => { // ... 其他代码 ... const tokenResponse = await axiosInstance.post( discovery.tokenEndpoint, qs.stringify(data), { headers: { "Content-Type": "application/x-www-form-urlencoded", } } ) // ... 其他代码 ... }
测试代码中mock这个导出的实例:
// 测试文件 import { axiosInstance, AuthManager } from './AuthManager'; import MockAdapter from 'axios-mock-adapter'; it("obtains an access code and a refresh token from the authorization code", async () => { const mockAxiosAdapter = new MockAdapter(axiosInstance); // 注意:reply的响应数据不需要包裹在data字段中,axios会自动处理 const mockTokenData = { access_token: "DEF", refresh_token: "ABC" }; const authResult = { type: "success", params: { code: "123" } } const authRequest = { codeVerifier: "XYZ" } mockAxiosAdapter.onPost(AuthManager.discovery.tokenEndpoint).reply(200, mockTokenData); await AuthManager.getTokenRequest(authResult as AuthSessionResult, authRequest as AuthRequest); expect(AsyncStorage.setItem).toHaveBeenCalledWith("access_token", "DEF"); expect(AsyncStorage.setItem).toHaveBeenCalledWith("refresh_token", "ABC"); })
2. 宽松匹配请求(可选)
如果请求体或URL存在动态内容,可以用正则表达式匹配URL,或忽略请求体校验:
// 用正则匹配URL后缀 mockAxiosAdapter.onPost(/\/api\/token$/).reply(200, mockTokenData); // 或仅匹配请求方法和URL,忽略其他细节 mockAxiosAdapter.onPost(AuthManager.discovery.tokenEndpoint).reply(200, mockTokenData);
3. 验证mock触发状态
在测试中添加断言,确认请求是否命中mock:
await AuthManager.getTokenRequest(authResult as AuthSessionResult, authRequest as AuthRequest); // 确认POST请求被触发了一次 expect(mockAxiosAdapter.history.post.length).toBe(1);
内容的提问来源于stack exchange,提问作者fiocotti
相关产品推荐
相关产品推荐

