[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-axios
库 Axios
实例模块相关业务逻辑的测试,下一节课我们会对拦截器模块做测试。
拦截器模块单元测试
拦截器是 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
模块的业务逻辑。