ctimmerm / axios-mock-adapter

Axios adapter that allows to easily mock requests
MIT License
3.47k stars 245 forks source link

Wrong history of request when use an interceptor to re-send request with changed config #348

Open eagleliang opened 2 years ago

eagleliang commented 2 years ago

versions:

The history of request's config seems to be overwrite if it belongs to the same request.

Here is the demo code: I create a axios instance which will update the request's headers if server's response's data said statusCode is 401 (NOTE: not http status code), and re-send the request with the updated config.

The expectation is the first request header should not as same as the second. But the assertion always failed ( For reading convenience, I put the failed assertion at the end of the test case)

------------ netadapter.ts ----------------

import axios, {AxiosRequestHeaders} from "axios";

export class NetAdapter {
    #accessToken: string | undefined = undefined
    #instance = axios.create({
        baseURL: 'http://192.168.1.1'
    })

    constructor() {
        this.#instance.interceptors.response.use(async (res) => {
            const originalConfig = res.config

            if (res.status === 200 && res.data?.statusCode === 401) {
                if (originalConfig.headers) {
                    originalConfig.headers = {
                        ...originalConfig.headers,
                        accessToken: '123456',
                    }
                } else {
                    originalConfig.headers = {
                        accessToken: '123456'
                    }
                }

                return await this.#instance.request(originalConfig)
            }
            return res
        })
    }

    async call(api: string) {
        const headers: AxiosRequestHeaders = this.#accessToken ? {accessToken: this.#accessToken} : {}
        return await this.#instance.post(api, {}, {headers: headers})
    }

    setToken(token: string) {
        this.#accessToken = token
    }
}

-------------------- the test suits ---------------------

import {NetAdapter} from "../src/netadapter";
import MockAdapter from "axios-mock-adapter";
import axios from "axios";
import exp from "constants";

let adapter!: NetAdapter;
let mockAxios!: MockAdapter;

beforeAll(() => {
    mockAxios = new MockAdapter(axios, {delayResponse: 200})
})

beforeEach(() => {
    mockAxios.reset()
    adapter = new NetAdapter()
})

describe('axios mock test', () => {
    test('auto add token', async () => {
        const expectUrl = '/abc'
        mockAxios.onPost().replyOnce(200, {statusCode: 401}).onPost().replyOnce(200)

        const res = await adapter.call(expectUrl)
        expect(mockAxios.history.post.length).toBe(2)
        expect(mockAxios.history.post[0].url).toContain(expectUrl)
        expect(mockAxios.history.post[1].url).toContain(expectUrl)
        expect(mockAxios.history.post[1]?.headers?.accessToken).toEqual('123456')
        expect(mockAxios.history.post[0]?.headers?.accessToken).toBeUndefined()
    })

    test('auto update token', async () => {
        const expectUrl = '/abc'
        mockAxios.onPost().replyOnce(200, {statusCode: 401}).onPost().replyOnce(200)
        adapter.setToken('666')

        const res = await adapter.call(expectUrl)
        expect(mockAxios.history.post.length).toBe(2)
        expect(mockAxios.history.post[0].url).toContain(expectUrl)
        expect(mockAxios.history.post[1].url).toContain(expectUrl)
        expect(mockAxios.history.post[1]?.headers?.accessToken).toEqual('123456')
        expect(mockAxios.history.post[0]?.headers?.accessToken).toEqual('666')
    })

    test('manual set token and send request instead of automation', async () => {
        const expectUrl = '/abc'
        const expectToken = 'fff'

        mockAxios.onPost().reply(200)
        await adapter.call(expectUrl)
        adapter.setToken(expectToken)
        const res = await adapter.call(expectUrl)

        expect(mockAxios.history.post.length).toBe(2)
        expect(mockAxios.history.post[0]?.headers?.accessToken).toBeUndefined()
        expect(mockAxios.history.post[0].url).toContain(expectUrl)
        expect(mockAxios.history.post[1]?.headers?.accessToken).toEqual(expectToken)
        expect(mockAxios.history.post[1].url).toContain(expectUrl)
    })
})

-------------- the test results -------------------

  FAIL  test/NetAdapter.test.ts
  axios mock test
    ✕ auto add token (423 ms)
    ✕ auto update token (414 ms)
    ✓ manual set token and send request instead of automation (417 ms)

  ● axios mock test › auto add token

    expect(received).toBeUndefined()

    Received: "123456"

      26 |         expect(mockAxios.history.post[1].url).toContain(expectUrl)
      27 |         expect(mockAxios.history.post[1]?.headers?.accessToken).toEqual('123456')
    > 28 |         expect(mockAxios.history.post[0]?.headers?.accessToken).toBeUndefined()
         |                                                                 ^
      29 |     })
      30 |
      31 |     test('auto update token', async () => {

      at test/NetAdapter.test.ts:28:65
      at fulfilled (test/NetAdapter.test.ts:5:58)

  ● axios mock test › auto update token

    expect(received).toEqual(expected) // deep equality

    Expected: "666"
    Received: "123456"

      39 |         expect(mockAxios.history.post[1].url).toContain(expectUrl)
      40 |         expect(mockAxios.history.post[1]?.headers?.accessToken).toEqual('123456')
    > 41 |         expect(mockAxios.history.post[0]?.headers?.accessToken).toEqual('666')
         |                                                                 ^
      42 |     })
      43 |
      44 |     test('manual set token and send request instead of automation', async () => {

      at test/NetAdapter.test.ts:41:65
      at fulfilled (test/NetAdapter.test.ts:5:58)

Test Suites: 1 failed, 1 total
Tests:       2 failed, 1 passed, 3 total
Snapshots:   0 total
Time:        5.753 s
Ran all test suites.
error Command failed with exit code 1.