简体   繁体   中英

Testing promises within promises

I used to have the following code:

function makeCall(userInfo) {
  api.postUser(userInfo).then(response => {
    utils.redirect(response.url);
  })

  // other logic
  return somethingElse;
}

And I was able to write a test that looked like this:

const successPromise = Promise.resolve({ url: 'successUrl' })

beforeEach(function() {
  sinon.stub(api.postUser).returns(successPromise);
}

afterEach(function() {
  api.postUser.restore();
}

it "calls API properly and redirects" do
  makeCall({});  
  expect(api.postUser).calledWith(userInfo).toBe(true);
  successPromise.then(() => {
    expect(utils.redirect.calledWith('successUrl')).toBe(true);
    done();
  }
emd

And everything was green.

Now, I had to add another promise to make another external call, before doing the api postUser call, so my code looks like this:

function makeCall(names) {
  fetchUserData(names).then(userData => {
    return api.postUser(userData).then(response => {
     utils.redirect(response.url);
    })
  })

  // other logic
  return somethingElse;
 }

where fetchUseData is a chain of many promises, such like:

function fetchNames(names) {
  // some name regions
  return Promise.all(names);
}
function fetchUserData(names) {
  fetchUsersByNames(names).then(users => {
    // For now we just choose first user
    {
      id: users[0].id,
      name: users[0].name,
    }
  });
}

And the tests I had fail. I am trying to understand how to change my tests to make sure that I am still testing that I do the final API call properly and the redirect is also done. I want to stub what fetchUserData(names) , to prevent doing that HTTP call.

You should add a return statement, otherwise you are not returning promises nowhere:

function fetchNames(names) {
  // some name regions
  return Promise.all(names);
}
function fetchUserData(names) {
  return fetchUsersByNames(names).then(users => {
    // For now we just choose first user
    {
      id: users[0].id,
      name: users[0].name,
    }
  });
}

So when you are using Promise.all(), then you will have as result of the promise an array with all the value returned by all the promises. So then this method should look like this when called:

fetchNames(names).then((arrayOfResolvedPromises) => {
 // here you will have all your promised resolved and the array holds all the results
});

So inside your test you can move your done inside the block where all the promises will be resolved.

In addition, I strongly suggest you to use a library as chai-as-promised for testing promises. It has a lot of nice methods for testing your promises.

https://github.com/domenic/chai-as-promised

You're not using promises correctly. Your code doesn't have a single return statement, when it should have several (or it should be using arrow functions in such a way that you don't need them, which you're not doing).

Fix your code:

function makeCall(names) {
  // v---- return
  return fetchUserData(names).then(userData => {
    // v---- return
    return api.postUser(userData).then(response => {
      utils.redirect(response.url);
    })
  })
 }


function fetchUserData(names) {
  // v---- return
  return fetchUsersByNames(names).then(users => {
    // For now we just choose first user
    // v---- return
    return {
      id: users[0].id,
      name: users[0].name,
    }
  });
}

Once you've done that, you can have your test wait for all of the operations to finish.

Test code:

makeCall(['name']).then(() =>
  expect(api.postUser).calledWith(userInfo).toBe(true);
  expect(utils.redirect.calledWith('successUrl')).toBe(true);
  done();
});

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM