0

I had hard times testing promise based functions. I am using Mocha as test framework and chai library for assertion framework. I new to both Mocha and chai. I had few problems and I do not know that is wrong with my code.Maybe my testing approach totally wrong, maybe someone help em.

I get Uncaught (in promise) error for expect, but actually I do not know whether my way is the correct way of testing these functions.

Here is my returnResult function which resolves a value -> a string

var returnResult = function (stateParamName, stateParamValue) {

 return new Promise((resolve, reject) => {
   peer3.get({ path: { equals: 'todo/#0' } }).then(function (results) {
    console.log(stateParamName + ': ' + results[0].value[stateParamName])
    resolve(results[0].value[stateParamName]);
  });
});
}

And here is my mocha test

 describe("Call Tests Suite", function () {
  describe("Basic Call Test", function () {
  it("Call status for both side should be IN CALL", function () {
  peer3.call('login/add', ['testaccount1'])
    .then(() => peer3.call('todo/makeCall1', ['testaccount2@testdomain.com']))
    .then(() => checkResult('state_term', 'RINGING'))
    .then((interval) => { clearInterval(interval); peer3.call('todo/answerCall2', ['empty']) })
    .then(() => checkResult('state_term', 'IN_CALL'))
    .then((interval) => clearInterval(interval))
    //.then(console.log('test sonucu: ' + returnResult('state_term', 'IN_CALL')))
    .then(returnResult('state_term', 'IN_CALL'))
    .then((result) => expect(result).to.equal('IN_CALL'))        
   });

 });

As you see I am only using assert for last result. Maybe I should test whole test as an promise function. Can someone help me on this ?

pynexj
  • 19,215
  • 5
  • 38
  • 56
Rasim AVCI
  • 133
  • 2
  • 4
  • 11
  • 1
    Your `.then((interval) => ...)` does not return a promise, but it should. – Tomalak Nov 27 '17 at 07:54
  • 1
    Also, you commit the [explicit promise construction antipattern](https://stackoverflow.com/q/23803743/18771) in your first code sample. Don't use `new Promise()` there. Simply `return peer3.get()` directly. – Tomalak Nov 27 '17 at 07:56
  • 1
    Not sure where the uncaught error is coming from, but you need to `return` the promise (on the 4th line of your test code) so that Mocha can wait for it to finish. You're also failing to return a promise in the 3rd `then`. So the next `then` cannot wait for `peer3.call('todo/answerCall2', ['empty'])` to complete. – JLRishe Nov 27 '17 at 08:14

1 Answers1

0

I don't know from where your error come from. However, there are a lot of things in your code you can improve, and may lead you to a better debugging, so you can find the error:

1- You should add a .catch handler at the end of the promise chain. The 'uncaught error' refers to this: you have an error in one of your then handlers but it is not caught in a catch. You should add a catch call at the end of the train:

describe("Call Tests Suite", function () {
describe("Basic Call Test", function () {
it("Call status for both side should be IN CALL", function () {
peer3.call('login/add', ['testaccount1'])
    .then(() => peer3.call('todo/makeCall1', ['testaccount2@testdomain.com']))
    .then(() => checkResult('state_term', 'RINGING'))
    .then((interval) => { clearInterval(interval); peer3.call('todo/answerCall2', ['empty']) })
    .then(() => checkResult('state_term', 'IN_CALL'))
    .then((interval) => clearInterval(interval))
    //.then(console.log('test sonucu: ' + returnResult('state_term', 'IN_CALL')))
    .then(returnResult('state_term', 'IN_CALL'))
    .then((result) => expect(result).to.equal('IN_CALL'))
    .catch(err => {
      console.log(err);//This will allow you better debugging.
    })       
});

});

Ok, now, we have to keep in mind that your code is asynchronous. However, mocha it functions, by default, are synchronous: they will not wait for your async code to execute.

In order to tell mocha that your test is asynchronous, you have to pass a parameter to the test. This parameter is a function, usually called done, that you must explicitly call when your test finish. Otherwise, your test will finish before reaching the asynchronous part of your code, usually giving you false positives.

describe("Call Tests Suite", function () {
describe("Basic Call Test", function () {
it("Call status for both side should be IN CALL", function (done) {
peer3.call('login/add', ['testaccount1'])
    .then(() => peer3.call('todo/makeCall1', ['testaccount2@testdomain.com']))
    .then(() => checkResult('state_term', 'RINGING'))
    .then((interval) => { clearInterval(interval); peer3.call('todo/answerCall2', ['empty']) })
    .then(() => checkResult('state_term', 'IN_CALL'))
    .then((interval) => clearInterval(interval))
    //.then(console.log('test sonucu: ' + returnResult('state_term', 'IN_CALL')))
    .then(returnResult('state_term', 'IN_CALL'))
    .then((result) => expect(result).to.equal('IN_CALL'))
    .then( () => {
        done(); //dont use .then(done) or things may break due to extra 
        parameter
    })
    .catch( err => {
         console.log(err);
         done(err); //passing a parameter to done makes the test fail.
    })       
});

});

Still, there are an issue with your code we must address. The then method expects a function as a parameter. However, in this line: .then(returnResult('state_term', 'IN_CALL'))

You are passing it a call to returnResult('state_term', 'IN_CALL'), which return not a function, but a promise. You should pass a function instead -and then return the promise-:

.then(() => returnResult('state_term', 'IN_CALL')) //now the parameter to 
       //then is a function that return a Promise, not a Promise itself.

Also, as you've been told in the commments, you're explicitly returning a new Promise that wraps a Promise in your return result function: that is not needed at all and that function could be written as so:

var returnResult = function (stateParamName, stateParamValue) {
    return peer3.get({ path: { equals: 'todo/#0' } }).then(function (results) {
        console.log(stateParamName + ': ' + results[0].value[stateParamName]);
        return(results[0].value([stateParamName]));
    });
}

However, there are a lot of things in your code that seems really odd (I'am not sure at all why the setinterval calls are there), so I'm pretty confident the test will not work as you expect. You should start by familiarizing yourself with Promises and asynchronous mocha tests, and don't try to test really long sequences of asynchronous operations. good luck!

Sergeon
  • 6,638
  • 2
  • 23
  • 43
  • Thanks Sergeon for detailed answer I will try that but before that I wanted to mention something. I see there is a problem even without using mocha. Same code runs sequentially when I put inside a button listener, but not working sequentially -even if I remove expect part- when I put code directly in js page and run mocha. Really can not understand this -> why same piece of code not working when its not in a listener. Even I removed everything related to mocha but still same code does run in same order as it was in a button listener ?? really confused. – Rasim AVCI Nov 27 '17 at 13:35
  • Btw setinterval calls are for waiting bcos I do not know when value will be updated. – Rasim AVCI Nov 27 '17 at 15:32
  • Actually I was tried done before and again I tested it with your code. I get Uncaught (in promise) ReferenceError: done is not defined – Rasim AVCI Nov 27 '17 at 15:38
  • Did you add `done` as a parameter to the `it` callback function? – Sergeon Nov 27 '17 at 15:44
  • After adding done parameter in function call on it( ... line, I get this error this time. -> Error: Timeout of 2000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves. – Rasim AVCI Nov 27 '17 at 15:51