Tape: Can not handle promise in test

Created on 21 Aug 2017  路  14Comments  路  Source: substack/tape

I am trying to write test script for some DB interaction. But the script always fails with "test exited without ending" error in promise. I have tried using async-await, promise-then and timeOut. But it always exits with "test exited without ending" error. Please help me with that.
Below is my code -

let test = require('tape');
let tapSpec = require('tap-spec');
let mongoose = require('mongoose');
mongoose.Promise = require('bluebird');
import async from 'async';
import User from '../path-to-model';


test('Log In',(t)=>{
    let username = "username";
    let password = "password";  
        try{
            let user =  User.findOne({username: username}).select('password').exec();
            setTimeout(function() {
                if(user){
                    user.then((u)=>{
                        console.log('user',user);
                        t.pass('Got User');
                        //res(user);
                        t.end();
                    })          
                }
            }, 5000);
        } catch (e) {
            //rej(e);
            t.fail('Fail', e);
            t.end();
        }
})

Most helpful comment

Instead, use this:

test('Log In', async (t) =>{
    t.plan(2); // without this, tape stops waiting for your test on the first `await`
    const username = "username";
    const password = "password";
    try {
        const user =  await User.findOne({username: username}).select('password').exec();   
        t.equal(user.count, 1);
        t.pass('Got User!');
    } catch (e) {
        console.log('err', e)
        t.fail('Fail');
    }
    t.end();
});

All 14 comments

Before you do anything async, you have to use t.plan - otherwise tape can't know that your test has assertions left to run.

@ljharb Thank you for replying. I had tried with t.plan. But the promise is not handled anyway. The async-await does not give any value. If I use then, code inside then block never executes, and test ends with error.

test('Log In', (t) =>{
    t.plan(1); /// When trun on, gives plan != count error and test fails. When turned off gives 'test exited without ending' error
    let username = "username";
    let password = "password";
    let user =  User.findOne({username: username}).select('password').exec(); /// if i put async-await instead of then, i don't get any value form await.

    user.then((u)=>{
        //t.plan(2);
        console.log('user', user);
        t.equal(u.count, 1);
        t.pass('Got User!');
    });
    //t.pass();
});

That's because an async function is sugar for "a function that returns a promise" - and tape doesn't look at the return value of your function.

In other words, if you use async/await, then you have to await every promise, and use try/catch, and t.fail() in the catch block.

@ljharb even if I do that, I never get the value for the handled promise by await and test always ends in error with 'test exited without ending'

@c-gargi can you show me your full async/await example?

@ljharb here is my code with async-await

test('Log In', async(t) =>{
    let username = "username";
    let password = "password";
    try{ 
        let user =  await User.findOne({username: username}).select('password').exec();   
        t.equal(user.count, 1);
        t.pass('Got User!');
    } catch (e){
        console.log('err', e)
        t.fail('Fail');
    }
});

Instead, use this:

test('Log In', async (t) =>{
    t.plan(2); // without this, tape stops waiting for your test on the first `await`
    const username = "username";
    const password = "password";
    try {
        const user =  await User.findOne({username: username}).select('password').exec();   
        t.equal(user.count, 1);
        t.pass('Got User!');
    } catch (e) {
        console.log('err', e)
        t.fail('Fail');
    }
    t.end();
});

This gives "plan != count" error and test fails @ljharb

1 instead of 2 then :-) the t.plan(x) is still necessary, you just have to get the count right.

Does not help @ljharb , control is not reaching any assert after the const user = await User.findOne ... line, hence assert count is always 0. The error shows

operator: fail
expected: 1
actual: 0

If I put t.equal(1, 1); above the await test passes without executing everything after await

test('Log In', async (t) =>{
    t.plan(1); // without this, tape stops waiting for your test on the first `await`
    const username = "username";
    const password = "password";
    try {
        t.equal(1, 1);
        const user =  await User.findOne({username: username}).select('password').exec();   
        t.equal(user.count, 1);
        t.pass('Got User!');
    } catch (e) {
        console.log('err', e)
        t.fail('Fail');
    }
    t.end();
});

"operator: fail" means that it's hitting the catch.

t.plan(1) means that tape will mark the test as succeeded the instant it gets 1 passed assertion; it's got nothing to do with await.

Understood, but if the await is not working as expected, tape will never reach to the instant it gets 1 passed assertion.

It is working - it's generating an exception, and hitting your catch block.

I was just looking at whether tape is able to await async test functions (as an alternative to using plan()) and found this issue, but then also found https://github.com/parro-it/tape-async, which seems to resolve it. Just thought I'd leave it here in case anyone else was looking into it.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

t3hmrman picture t3hmrman  路  7Comments

shaunmcarmody picture shaunmcarmody  路  7Comments

cagross picture cagross  路  5Comments

dcousens picture dcousens  路  8Comments

dcousens picture dcousens  路  3Comments