4

How to make my test wait for the result of my api? I'm using vue and jest to test my components.

I want to test the method that writes a client to my database. In my component I have the following method:

methods: {

  onSubmitClient(){

   axios.post(`urlApi`, this.dados).then(res => {

      return res;
   })

  }
}

in my test

describe('login.vue', () => {

  let wrapper

  beforeAll(()=>{

    wrapper = mount(client, {
      stubs: ['router-link'],
      store,
      data() {
          return {
              dados: {
                  name: 'tes name',
                  city: 'test city'
              },
          };
      }
    })
  })

  it('store client', () => {

    res = wrapper.vm.onSubmitLogin()
    console.log(res);

  })

})

My test does not wait for the API call to complete. I need to wait for the API call to know if the test worked. How can I make my test wait for API return?

Canta
  • 1,480
  • 1
  • 13
  • 26
Renato Souza de Oliveira
  • 4,236
  • 4
  • 20
  • 31
  • What do you mean wait for the API call? Are you making actual requests in a unit test? – jonrsharpe Aug 12 '18 at 20:43
  • Yes. I'm making real requests. I want to know if my component is saving the data in my api. – Renato Souza de Oliveira Aug 12 '18 at 20:47
  • 1
    That's *not* a unit test, you should be stubbing out the client for this. If you want to test real requests against a real backend, use E2E testing at a higher level. – jonrsharpe Aug 12 '18 at 20:50
  • @jonrsharpe but you need a test framework for that too :). You'll hit this problem at some point, somewhere. Either by polling, or subscribing to an event or proxying the actual request to inspect what request is being made... It's a valid question. – jessehouwing Aug 12 '18 at 21:06
  • I started the tests using e2e nightwatchjs. Only I could not perform the tests, because nightwatchjs can not select a select from element.io. The select of the element.io is a ul li so it can not select. I decided to test with jest, but it's also difficult. – Renato Souza de Oliveira Aug 12 '18 at 21:11
  • @jessehouwing the OP's code doesn't return anything (although that's not necessarily their intent...) so can be tested synchronously. E2E testing tools have many more options for waiting for things to happen, due to their nature. In any case the jest docs cover it: https://jestjs.io/docs/en/asynchronous.html – jonrsharpe Aug 12 '18 at 21:11

1 Answers1

8

There are several issues in your code.

First, you cannot return from an async call. Instead, you should be probably setting up some data in your onSubmitClient, and returning the whole axioscall, which is a Promise. for instance:

onSubmitClient(){
  return axios.post(`urlApi`, this.dados).then(res => {
      this.result = res;
      return res;
  })
}

I assume the method here is storing a result from the server. Maybe you don't want that; it is just an example. I'll come back to it later.

Ok, so now, you could call onSubmitClient in your wrapper and see if this.result is already set. As you already know, this does not work straightforward.

In order for a jest test to wait for asynchronous code, you need either to provide a done callback function or return a promise. I'll show an example with the former:

it('store client', (done) => {
  wrapper.vm.onSubmitLogin().then((res) => {
    expect(wrapper.vm.dados).toEqual(res);
    done();
  })
});

Now this code should just work, but still there is an issue with it, as @jonsharpe says in a comment.

You usually don't want to perform real network requests in unitary tests because they are slow and unrealiable. Also, unitary tests are meant to test components in isolation, and here we are testing not only that our component sets this.result properly when the request is made. We are also testing that there is a webserver up and running that is actually working.

So, what I would do in this scenario to test that single piece of functionality, is to extract the request to another method, mock it with vue-test-utils and jest.fn, and then assert that onSubmitClient does its work:

The component:

export default {
  data() {
    return {
      http: axios,
      ...
    },
    methods: {

      onSubmitClient(){
        this.http.post(`urlApi`, this.dados).then(res => {
            this.result = res;
        })
      }
    }
  }
}

The test:

it('store client', (done) => {
  const fakeResponse = {foo: 'bar'};
  var post = jest.fn();
  var http : {
    post,
  };
  var wrapper = mount(client, {
    stubs: ['router-link'],
    store,
    data() {
      return {
        dados: {
            name: 'tes name',
            city: 'test city'
        },
        http, //now, the component under test will user a mock to perform the http post request.
      }
      }
  });
  wrapper.vm.onSubmitLogin().then( () => {
    expect(post).toHaveBeenCalled();
    expect(wrapper.vm.result).toEqual(fakeResponse);
    done();
  })

});

Now, your test asserts two things:

  1. post gets called.
  2. this.result is set as it should be.

If you don't want to store anything in your component from the server, just drop the second assertion and the this.result = res line in the method.

So basically this covers why your test is not waiting for the async request and some issues in your code. There are still some things to consider (f.i. I think a global wrapper is bad idea, and I would always prefer shallowMount over mount when testing components behavior), but this answer should help you a lot.

PS: didn't test the code, so maybe I messed up something. If the thing just doesn't work, look for syntax errors or similar issues.

Sergeon
  • 6,638
  • 2
  • 23
  • 43
  • Thank @Sergeon, lovely answer and showing Renato good way forward. I won't suppose asking more question would have lead him on this track. – jessehouwing Aug 16 '18 at 07:48