请求取消模块是 ts-axios 库核心流程其中一个分支,也是非常重要的模块,我们将从基础库和业务流程模块 2 个方面去编写单元测试。
Cancel 类单元测试
cancel/Cancel.spec.ts:
import Cancel, { isCancel } from '../../src/cancel/Cancel'describe('cancel:Cancel', () => {test('should returns correct result when message is specified', () => {const cancel = new Cancel('Operation has been canceled.')expect(cancel.message).toBe('Operation has been canceled.')})test('should returns true if value is a Cancel', () => {expect(isCancel(new Cancel())).toBeTruthy()})test('should returns false if value is not a Cancel', () => {expect(isCancel({ foo: 'bar' })).toBeFalsy()})})
CancelToken 类单元测试
cancel/CancelToken.spec.ts:
import CancelToken from '../../src/cancel/CancelToken'import Cancel from '../../src/cancel/Cancel'import { Canceler } from '../../src/types'describe('CancelToken', () => {describe('reason', () => {test('should returns a Cancel if cancellation has been requested', () => {let cancel: Cancelerlet token = new CancelToken(c => {cancel = c})cancel!('Operation has been canceled.')expect(token.reason).toEqual(expect.any(Cancel))expect(token.reason!.message).toBe('Operation has been canceled.')})test('should has no side effect if call cancellation for multi times', () => {let cancel: Cancelerlet token = new CancelToken(c => {cancel = c})cancel!('Operation has been canceled.')cancel!('Operation has been canceled.')expect(token.reason).toEqual(expect.any(Cancel))expect(token.reason!.message).toBe('Operation has been canceled.')})test('should returns undefined if cancellation has not been requested', () => {const token = new CancelToken(() => {// do nothing})expect(token.reason).toBeUndefined()})})describe('promise', () => {test('should returns a Promise that resolves when cancellation is requested', done => {let cancel: Cancelerconst token = new CancelToken(c => {cancel = c})token.promise.then(value => {expect(value).toEqual(expect.any(Cancel))expect(value.message).toBe('Operation has been canceled.')done()})cancel!('Operation has been canceled.')})})describe('throwIfRequested', () => {test('should throws if cancellation has been requested', () => {let cancel: Cancelerconst token = new CancelToken(c => {cancel = c})cancel!('Operation has been canceled.')try {token.throwIfRequested()fail('Expected throwIfRequested to throw.')} catch (thrown) {if (!(thrown instanceof Cancel)) {fail('Expected throwIfRequested to throw a Cancel, but test threw ' + thrown + '.')}expect(thrown.message).toBe('Operation has been canceled.')}})test('should does not throw if cancellation has not been requested', () => {const token = new CancelToken(() => {// do nothing})token.throwIfRequested()})})describe('source', () => {test('should returns an object containing token and cancel function', () => {const source = CancelToken.source()expect(source.token).toEqual(expect.any(CancelToken))expect(source.cancel).toEqual(expect.any(Function))expect(source.token.reason).toBeUndefined()source.cancel('Operation has been canceled.')expect(source.token.reason).toEqual(expect.any(Cancel))expect(source.token.reason!.message).toBe('Operation has been canceled.')})})})
注意,这里我们使用了 fail 函数表示一个测试的失败,这个并未在 Jest 文档中体现,但它是一个可以用的 API。
Cancel 业务逻辑单元测试
cancel.spec.ts:
import axios from '../src/index'import { getAjaxRequest } from './helper'describe('cancel', () => {const CancelToken = axios.CancelTokenconst Cancel = axios.CancelbeforeEach(() => {jasmine.Ajax.install()})afterEach(() => {jasmine.Ajax.uninstall()})describe('when called before sending request', () => {test('should rejects Promise with a Cancel object', () => {const source = CancelToken.source()source.cancel('Operation has been canceled.')return axios.get('/foo', {cancelToken: source.token}).catch(reason => {expect(reason).toEqual(expect.any(Cancel))expect(reason.message).toBe('Operation has been canceled.')})})})describe('when called after request has been sent', () => {test('should rejects Promise with a Cancel object', done => {const source = CancelToken.source()axios.get('/foo/bar', {cancelToken: source.token}).catch(reason => {expect(reason).toEqual(expect.any(Cancel))expect(reason.message).toBe('Operation has been canceled.')done()})getAjaxRequest().then(request => {source.cancel('Operation has been canceled.')setTimeout(() => {request.respondWith({status: 200,responseText: 'OK'})}, 100)})})test('calls abort on request object', done => {const source = CancelToken.source()let request: anyaxios.get('/foo/bar', {cancelToken: source.token}).catch(() => {expect(request.statusText).toBe('abort')done()})getAjaxRequest().then(req => {source.cancel()request = req})})})describe('when called after response has been received', () => {test('should not cause unhandled rejection', done => {const source = CancelToken.source()axios.get('/foo', {cancelToken: source.token}).then(() => {window.addEventListener('unhandledrejection', () => {done.fail('Unhandled rejection.')})source.cancel()setTimeout(done, 100)})getAjaxRequest().then(request => {request.respondWith({status: 200,responseText: 'OK'})})})})})
注意这里我们使用了 done.fail 表示了一个异常的结束,这个并未在 Jest 文档中体现,但它是一个可以用的 API。
至此,我们完成了取消模块相关业务逻辑的单元测试,我们测试覆盖率达到了阈值,测试已经通过了。但是扔未达到我们的目标,还有很多 feature 是没有覆盖到的。接下来我们就完成剩余 feature 的编写单元测试。
