11

I use axios-mock-adapter to mock my API, it works correctly but on one mock it returns a 404 error and I cannot found why.

There is here the sandbox with test, you can see when we run the test, the second check failed because the axios POST call haven't be mock. I have try to remove the header part but the sandbox has just crash when I have run test.


Mock of API (test part):

import axios from "axios";
import MockAdapter from 'axios-mock-adapter';
import Utils from "../Utils/Utils";

// Global variable for post request with axios
global.users_post = axios.create({
  baseURL: "http://localhost:5000/api/",
  headers: {'Content-Type': 'application/json'}
});

/* Mockup API */
var userMock = new MockAdapter(users_post);

const user_resp_full = {
  data: {
    first_name: "Test",
    last_name: "Test",
    email: "test@gmail.com",
    address: "Test",
    zipcode: 1010,
    city: "Test",
    admin: false
  }
}

const testAPI = () => {
    userMock
      .onPost("users", user_resp_full, Utils.getAuth())
      .reply(200, {data: {status: "success"}});
}

test("something", async () => {
  let tree = shallow(<UserManage type="create" uuid="" />);
  testAPI();
  await flushPromises();
  // Some test

  tree.find("#confirm-create").simulate("click");
  await flushPromises();
  // Error 404, mock isn't trigger
})

I have already check, data is the same, same for endpoint but it seems doesn't mock it correctly.


Axios call in class:

function (fields) {
    users_post.post("users", fields, Utils.getAuth())
    .then(resp => {
      let data = resp.data;
      // Do something
    })
    .catch(resp => {
      let data = resp.response.data;
      // Display error
    });
}

At this point, in my Jest test it returns a 404 error, so it hasn't mock my endpoint API (Other works).
The Utils.getAuth() function returns a header with a auth token.


Data send

That concerns content of data send (First is before the test call with mock, second is in the tested function and data log is the data send to api):

console.log src/tests/UserManage.test.js:222
  POST USER 2
{"first_name":"Test","last_name":"Test","email":"test@gmail.com","address":"Test","zipcode":1010,"city":"Test","admin":false}
console.log src/Components/Users/UserManage.js:152
  POST USER
console.log src/Components/Users/UserManage.js:153
{"first_name":"Test","last_name":"Test","email":"test@gmail.com","address":"Test","zipcode":1010,"city":"Test","admin":false}


Update

This error happen only when I use a POST request with a header like that:

axios.post("http://localhost/api/user/update", {name: "Test"}, {headers: {"Authorization": "Bearer token")}});

I have see on axios-mock-adapter github test page that eventually we should put headers in test without the label before:
{headers: {Autorization: "Bearer token"}} become {Autorization: "Bearer token"}
But unfortunately it doesn't work better than I have.


Solution

With the response of Matt Carlotta and his codesandbox, I modify mine with 2 examples of fixed issue:

  • A test of POST request mock using axios*
  • A test of POST request mock using an instance of axios*

* With axios-mock-adapter

Tryliom
  • 895
  • 1
  • 12
  • 37
  • Maybe totally unrelated, but in your testApi function, nothing is returned. In fact I don't see the point of the testApi function, what if you put your userMock.onPost()... directly in your test("something") ? – Bernard Pagoaga Mar 26 '19 at 13:16
  • Thanks for your comment, I test that but unfortunately doesn't work. I use that function for other mockup of other endpoint and they work. – Tryliom Mar 26 '19 at 14:33
  • have you tried adding a leading slash to your onPost call, '/users' ? the examples all show leading forward slashes, https://github.com/ctimmerm/axios-mock-adapter – N3SS4H Mar 26 '19 at 16:20
  • @N3SS4H Yes but I use an axios config that have in baseURL the..base url, like `http://localhost:5000/` and I test it but doesn't work. – Tryliom Mar 26 '19 at 16:33

2 Answers2

4

Alrighty, round two.

  • Your flushPromises function isn't resolving promises properly when that promise takes some time to respond. The workaround is to return the promise and stick an await in front of it within the .test.js file. Since we're using await on the promise, await flushPromises() isn't needed.
  • In addition, including the headers within the onPost mocked function will cause the function to throw an error. Since you're just mocking this request (and not actually testing its integration), you don't need to include them. However, since you're already using a custom axios configuration anyway, you can just include the headers in the axiosConfig.js file. See the working example of your codesandbox for more information.

As demonstrated in the Unit Testing codesandbox below, if you try to use await flushPromises() on the deleteUserDataOverTime method, it fails. It fails because it didn't resolve the promise. This promise takes some time to resolve and isn't being handled properly.

In addition, due to the asynchronous nature of the tests, you shouldn't include unit and integration tests within the same test file. Since the tests are asynchronous, calling mockAxios.reset() or mockAxios.restore() on the same mocked request or same mocked instance -- to make any additional real or fake API calls -- can and will inadvertently impact all the API calls (again they're asynchronous, not synchronous tests).

Working example of Unit testing an API: https://codesandbox.io/s/6z36z6pzyr (fake API -- includes GET, PUT, POST and DELETE)

Working example of Integration testing an API: https://codesandbox.io/s/7z93xnm206 (real API -- only includes GET, but functionality should remain the same for PUT, POST, and DELETE)

Working example of your codesandbox: https://codesandbox.io/s/526pj28n1n

Matt Carlotta
  • 18,972
  • 4
  • 39
  • 51
  • Your response doesn't real help me for my issue, you describe test with `GET` request but no one about a `POST` request with header that can be the error. I will however create a test who can be reproduce. – Tryliom Mar 28 '19 at 16:58
  • Yes it matter because my `GET` request (The same in your test) in my test works perfectly but it wasn't the case with my `POST` request which all failed. So I search why the `POST` request failed but I'm going to create a sandbox test with that. – Tryliom Mar 28 '19 at 17:04
  • I have add a sandbox link. – Tryliom Mar 28 '19 at 17:36
  • Please read carefully: An API implementation test (aka `Unit Test`) will **mimic** the `request`, the `side effect` and the `result`. An API integration test (aka `End to End Test`) will actually **test** the `request`, the `side effect` and the `result` from client to API. I've also updated my answer above to highlight the differences. – Matt Carlotta Mar 28 '19 at 19:03
  • Pretty sure I figured out your problem. See the updated answer above. – Matt Carlotta Mar 29 '19 at 21:19
  • After many test, I modify my codesandbox without the unnecessary surplus with only 2 test: One with the axios basic without instance and the second with an instance of axios. The only thing I should change is to update the global var of axios when my token change, by this way it will fix all my issue, thanks. – Tryliom Apr 01 '19 at 09:26
  • In fact, I have a problem, on my [codesandbox](https://codesandbox.io/s/3qmx7x3ovm?fontsize=14) (question updated), when I do my test, if we see the console, there is no headers send, it seems to be ignore. For my application test, they always failed but have "headers" in their request, do you know why ? I change my application to update all axios config when the token change in order to put the token in the header config. – Tryliom Apr 01 '19 at 13:32
  • In this case, the `headers` are located in `config.headers`. – Matt Carlotta Apr 01 '19 at 14:31
3

Okay, this was a tricky one. The issue is on the axios-mock-adapter package. It requires an instance of axios using the .create() method. See here: creating an instance

In your App.js, use:

import axios from "axios";
const instance = axios.create();

instance.post("http://localhost/api/user/update", {name: "Test"}, {headers: {"Authorization": "Bearer token")}});

Nothing needs to be changed in the tests though.

I got the hint from tests of axios-mock-adapter.

An example of such is: post test

aitchkhan
  • 1,842
  • 1
  • 18
  • 38
  • Like on github of `axios-mock-adapter`, I send you the [CodeSandbox](https://codesandbox.io/embed/3qmx7x3ovm) updated with your solution but it doesn't work. Furthermore, my issue use already an instance of axios with custom config and doesn't work too. – Tryliom Mar 29 '19 at 16:33