Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix tape: test emitter in async function #503

Closed
wants to merge 1 commit into from
Closed

fix tape: test emitter in async function #503

wants to merge 1 commit into from

Conversation

coderaiser
Copy link

Would be amazing if Promise not marked end of the test because it breaks such codeб calling end twice:

const {EventEmitter} = require('events');
const test = require('../../');

test('async6: double end', async function myTest(t) {
    const events = new EventEmitter();

    events.once('change', () => {
        t.pass('good');
        t.end();
    });

    setTimeout(() => {
        events.emit('change');
    });
});

Using subscription to events is a very common situation for end to end tests :).
Anyways I want to thank you for a v5.0.0 and for a fix of rejected promises, I used try-to-tape to fix this behavior :).

coderaiser added a commit to coderaiser/supertape that referenced this pull request Apr 25, 2020
@Raynos
Copy link
Collaborator

Raynos commented Apr 25, 2020

Auto calling "end" is a nice feature but breaks tests mixing promises and callbacks.

Honestly, mixing promises and callbacks is bad.

You should try writing tests like

test('async6: double end', async function myTest(t) {
    const events = new EventEmitter();

    setTimeout(() => {
        events.emit('change');
    });

   await new Promise((resolve) => {
       events.once('change', () => {
            t.pass('good');
            resolve();
        });
    })
});

@coderaiser
Copy link
Author

coderaiser commented Apr 25, 2020

Auto calling "end" is a nice feature but breaks tests mixing promises and callbacks.

Actually, if I want this feature I would take any other test runner 😄 :

All of them gives ability of auto calling "end".
One of the things I like in tape is ability to explicitly set ending of a test.

Honestly, mixing promises and callbacks is bad.

Could you please describe in more details why it's bad? My code is shorter, and I test this way a lot modules, and everything works good: working well tested simple to support code.

Also that was illustrative example of course I would rather choose such way of code:

import EventEmitter, {once} from 'events';
import holdUp from '@iocmd/hold-up';
import test from 'tape';

test('async6: double end', async (t) => {
     const emitter = new EventEmitter();
     const emitChange = emitter.emit.bind(emitter, 'change');

     await Promise.all([
         once(emitter, 'change'),
         holdUp(emitChange),
     ]);

     t.pass('good');
     t.end();
});

Using once and hold-up, but I can't change all my codebase in a moment :), but I would like to use latest version of tape with rejects fixed, for example :)

And also this is details of realization tape, it's leaking abstraction, and all this just to have ability not write end :)?

@Raynos
Copy link
Collaborator

Raynos commented Apr 25, 2020

This was discussed in the PR that added async function support ( #472 (comment) ).

Specifically #472 (comment)

As a work around you can add

test('foo', async function t(assert) => {


  await new Promise((resolve) => {
    assert.once('end', resolve)
  })
})

Basically for any test where you do not want auto end behavior add an await on the end event at the bottom of the test.

@coderaiser
Copy link
Author

OK, I understood that it is a well designed feature to use one promise for a full test lifecycle. Anyways it makes test harder to write and maintain, and breaks existing well-working tests. As a workaround supertape can be used, it is already extends tapes features list.

@Raynos
Copy link
Collaborator

Raynos commented Apr 25, 2020

If you want to make tests easier to write and maintain then dont use async/await in a callback driven test.

Aka

const {EventEmitter} = require('events');
const test = require('../../');

// Use vanilla function without async / await for testing callbacks.
test('async6: double end', function myTest(t) {
    const events = new EventEmitter();

    events.once('change', () => {
        t.pass('good');
        t.end();
    });

    setTimeout(() => {
        events.emit('change');
    });
});

@ljharb
Copy link
Collaborator

ljharb commented Apr 25, 2020

Indeed; these were all explicit decisions made.

If your test is an async function, the nature of async function is that it returns a promise, and so your entire test ends when that promise fulfills - which is how all other popular test runners work when receiving a promise.

I'm going to close this, since all the removed tests were added for a reason :-)

@ljharb ljharb closed this Apr 25, 2020
@ljharb ljharb added the wontfix label Apr 25, 2020
@Raynos
Copy link
Collaborator

Raynos commented Apr 25, 2020

This is a good example of breaking people's existing tests on tape @ 4 in the wild.

Adding a section to the README about migrating to v5 and end() already called might be useful.

@coderaiser
Copy link
Author

@ljharb that's an interesting api change and it has sense. Would be great if you write about such changes in release notes, so consumers can read about what changes is waiting for them, and why :). Also message end is already called is not not intuitive :), message describing promise behavior with a link to issue would be much better (and deprecation notice in v4, so people can upgrade more smoothly).

@ljharb
Copy link
Collaborator

ljharb commented Apr 25, 2020

@coderaiser https://github.com/substack/tape/releases/tag/v5.0.0 is the release notes; the amount of prose that would be necessary to avoid all misunderstandings in any project is potentially very massive, and it's not imo reasonable to expect that :-)

Separately, v4 is NOT deprecated - deprecation messages are not simply to encourage updates. v4 will work fine likely forever, and there's no reason anybody needs to upgrade.

@coderaiser
Copy link
Author

Separately, v4 is NOT deprecated - deprecation messages are not simply to encourage updates. v4 will work fine likely forever, and there's no reason anybody needs to upgrade.

@ljharb I didn't say that v4 is deprecated, I say that ability to work with async code, the old behavior is deprecated :).

the amount of prose that would be necessary to avoid all misunderstandings in any project is potentially very massive, and it's not imo reasonable to expect that :-)

Here is an example of release notes with well defined breaking changes :), I also have a lot packages, but that doesn't mean that breaking change I make should be invisible, if you have new vision on the way how library should be used, it's a good practice to describe this vision in some convenient way (I admit that our dialog can be this way, I'm OK with it, but that's no scaled).

Anyways I appreciate new knowledge I received today, and now my tests looks this way :).

@ljharb
Copy link
Collaborator

ljharb commented Apr 26, 2020

The ability to mix a Promise-returning test callback, with non-Promise code, has always been deprecated from a community best practices sense ¯\_(ツ)_/¯

@coderaiser
Copy link
Author

Just created codemod to simplify transition to tape v5.0.0: convert-emitter-to-promise. It can help to refactor mixed callbacks with promises :).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants