Skip to main content

[TS] ts-axios(22) - 单元测试(Axios实例模块与拦截器模块)


Axios实例模块单元测试

ts-axios 提供了 axios.create 静态方法,返回一个 instance 实例,我们需要对这个模块做测试。

测试代码编写

test/instance.spec.ts

import axios, { AxiosRequestConfig, AxiosResponse } from '../src/index'
import { getAjaxRequest } from './helper'

describe('instance', () => {
beforeEach(() => {
jasmine.Ajax.install()
})

afterEach(() => {
jasmine.Ajax.uninstall()
})

test('should make a http request without verb helper', () => {
const instance = axios.create()

instance('/foo')

return getAjaxRequest().then(request => {
expect(request.url).toBe('/foo')
})
})

test('should make a http request', () => {
const instance = axios.create()

instance.get('/foo')

return getAjaxRequest().then(request => {
expect(request.url).toBe('/foo')
expect(request.method).toBe('GET')
})
})

test('should make a post request', () => {
const instance = axios.create()

instance.post('/foo')

return getAjaxRequest().then(request => {
expect(request.method).toBe('POST')
})
})

test('should make a put request', () => {
const instance = axios.create()

instance.put('/foo')

return getAjaxRequest().then(request => {
expect(request.method).toBe('PUT')
})
})

test('should make a patch request', () => {
const instance = axios.create()

instance.patch('/foo')

return getAjaxRequest().then(request => {
expect(request.method).toBe('PATCH')
})
})

test('should make a options request', () => {
const instance = axios.create()

instance.options('/foo')

return getAjaxRequest().then(request => {
expect(request.method).toBe('OPTIONS')
})
})

test('should make a delete request', () => {
const instance = axios.create()

instance.delete('/foo')

return getAjaxRequest().then(request => {
expect(request.method).toBe('DELETE')
})
})

test('should make a head request', () => {
const instance = axios.create()

instance.head('/foo')

return getAjaxRequest().then(request => {
expect(request.method).toBe('HEAD')
})
})

test('should use instance options', () => {
const instance = axios.create({ timeout: 1000 })

instance.get('/foo')

return getAjaxRequest().then(request => {
expect(request.timeout).toBe(1000)
})
})

test('should have defaults.headers', () => {
const instance = axios.create({ baseURL: 'https://api.example.com' })

expect(typeof instance.defaults.headers).toBe('object')
expect(typeof instance.defaults.headers.common).toBe('object')
})

test('should have interceptors on the instance', done => {
axios.interceptors.request.use(config => {
config.timeout = 2000
return config
})

const instance = axios.create()

instance.interceptors.request.use(config => {
config.withCredentials = true
return config
})

let response: AxiosResponse
instance.get('/foo').then(res => {
response = res
})

getAjaxRequest().then(request => {
request.respondWith({
status: 200
})

setTimeout(() => {
expect(response.config.timeout).toEqual(0)
expect(response.config.withCredentials).toEqual(true)
done()
}, 100)
})
})

test('should get the computed uri', () => {
const fakeConfig: AxiosRequestConfig = {
baseURL: 'https://www.baidu.com/',
url: '/user/12345',
params: {
idClient: 1,
idTest: 2,
testString: 'thisIsATest'
}
}
expect(axios.getUri(fakeConfig)).toBe(
'https://www.baidu.com/user/12345?idClient=1&idTest=2&testString=thisIsATest'
)
})
})

至此我们完成了 ts-axiosAxios 实例模块相关业务逻辑的测试,下一节课我们会对拦截器模块做测试。


拦截器模块单元测试

拦截器是 ts-axios 库一个非常实用的功能,接下来我们来编写它的测试代码。

测试代码编写

test/interceptor.spec.ts

import axios, { AxiosRequestConfig, AxiosResponse } from '../src/index'
import { getAjaxRequest } from './helper'

describe('interceptors', () => {
beforeEach(() => {
jasmine.Ajax.install()
})

afterEach(() => {
jasmine.Ajax.uninstall()
})

test('should add a request interceptor', () => {
const instance = axios.create()

instance.interceptors.request.use((config: AxiosRequestConfig) => {
config.headers.test = 'added by interceptor'
return config
})

instance('/foo')

return getAjaxRequest().then(request => {
expect(request.requestHeaders.test).toBe('added by interceptor')
})
})

test('should add a request interceptor that returns a new config object', () => {
const instance = axios.create()

instance.interceptors.request.use(() => {
return {
url: '/bar',
method: 'post'
}
})

instance('/foo')

return getAjaxRequest().then(request => {
expect(request.method).toBe('POST')
expect(request.url).toBe('/bar')
})
})

test('should add a request interceptor that returns a promise', done => {
const instance = axios.create()

instance.interceptors.request.use((config: AxiosRequestConfig) => {
return new Promise(resolve => {
setTimeout(() => {
config.headers.async = 'promise'
resolve(config)
}, 10)
})
})

instance('/foo')

setTimeout(() => {
getAjaxRequest().then(request => {
expect(request.requestHeaders.async).toBe('promise')
done()
})
}, 100)
})

test('should add multiple request interceptors', () => {
const instance = axios.create()

instance.interceptors.request.use(config => {
config.headers.test1 = '1'
return config
})
instance.interceptors.request.use(config => {
config.headers.test2 = '2'
return config
})
instance.interceptors.request.use(config => {
config.headers.test3 = '3'
return config
})

instance('/foo')

return getAjaxRequest().then(request => {
expect(request.requestHeaders.test1).toBe('1')
expect(request.requestHeaders.test2).toBe('2')
expect(request.requestHeaders.test3).toBe('3')
})
})

test('should add a response interceptor', done => {
let response: AxiosResponse
const instance = axios.create()

instance.interceptors.response.use(data => {
data.data = data.data + ' - modified by interceptor'
return data
})

instance('/foo').then(data => {
response = data
})

getAjaxRequest().then(request => {
request.respondWith({
status: 200,
responseText: 'OK'
})

setTimeout(() => {
expect(response.data).toBe('OK - modified by interceptor')
done()
}, 100)
})
})

test('should add a response interceptor that returns a new data object', done => {
let response: AxiosResponse
const instance = axios.create()

instance.interceptors.response.use(() => {
return {
data: 'stuff',
headers: null,
status: 500,
statusText: 'ERR',
request: null,
config: {}
}
})

instance('/foo').then(res => {
response = res
})

getAjaxRequest().then(request => {
request.respondWith({
status: 200,
responseText: 'OK'
})

setTimeout(() => {
expect(response.data).toBe('stuff')
expect(response.headers).toBeNull()
expect(response.status).toBe(500)
expect(response.statusText).toBe('ERR')
expect(response.request).toBeNull()
expect(response.config).toEqual({})
done()
}, 100)
})
})

test('should add a response interceptor that returns a promise', done => {
let response: AxiosResponse
const instance = axios.create()

instance.interceptors.response.use(data => {
return new Promise(resolve => {
// do something async
setTimeout(() => {
data.data = 'you have been promised!'
resolve(data)
}, 10)
})
})

instance('/foo').then(res => {
response = res
})

getAjaxRequest().then(request => {
request.respondWith({
status: 200,
responseText: 'OK'
})

setTimeout(() => {
expect(response.data).toBe('you have been promised!')
done()
}, 100)
})
})

test('should add multiple response interceptors', done => {
let response: AxiosResponse
const instance = axios.create()

instance.interceptors.response.use(data => {
data.data = data.data + '1'
return data
})
instance.interceptors.response.use(data => {
data.data = data.data + '2'
return data
})
instance.interceptors.response.use(data => {
data.data = data.data + '3'
return data
})

instance('/foo').then(data => {
response = data
})

getAjaxRequest().then(request => {
request.respondWith({
status: 200,
responseText: 'OK'
})

setTimeout(() => {
expect(response.data).toBe('OK123')
done()
}, 100)
})
})

test('should allow removing interceptors', done => {
let response: AxiosResponse
let intercept
const instance = axios.create()

instance.interceptors.response.use(data => {
data.data = data.data + '1'
return data
})
intercept = instance.interceptors.response.use(data => {
data.data = data.data + '2'
return data
})
instance.interceptors.response.use(data => {
data.data = data.data + '3'
return data
})

instance.interceptors.response.eject(intercept)
instance.interceptors.response.eject(5)

instance('/foo').then(data => {
response = data
})

getAjaxRequest().then(request => {
request.respondWith({
status: 200,
responseText: 'OK'
})

setTimeout(() => {
expect(response.data).toBe('OK13')
done()
}, 100)
})
})
})

运行测试后我们发现在测试用例 should add a request interceptor that returns a new config object 报错了,是代码运行的报错,而不是测试期望结果的报错,顺着报错信息,我们可以找到报错原因。

core/xhr.ts 中,执行到 processHeaders 中的 Object.keys(headers).forEach 代码报错,因为我们在拦截器对请求配置做了修改,导致 headers 为空,所以报错。

于是我们在解构赋值 headers 的时候,给它添加默认值即可。

const {
// ...
headers = {}
} = config

再次运行测试,发现全部测试通过。

至此,我们完成了 ts-axios 库对拦截器模块的单元测试,下节课我们来测试 mergeConfig 模块的业务逻辑。