Nested tests block the stream? #130
Replies: 2 comments 3 replies
-
There are two disctincts things:
By default all the tests run concurrently and eagerly (thanks to this IIFE - immediately invoked function expression) as soon as possible. So if you write: sample program with logsimport { test } from 'zora';
const wait = (time) =>
new Promise((resolve) => {
setTimeout(() => resolve(), time);
});
test('test 1', async (t) => {
t.ok(true, 'hello 1');
console.log('1 done');
t.test('test 2', async (t) => {
t.ok(true, 'hello 2');
await wait(2000);
console.log('2 done');
});
t.ok(true, 'hello 3');
console.log('3 done');
}); you will see the log for the third test before the one for the second test which has not finished yet. For the reporting part, the message stream keeps the order as defined by the program: from a reporter perspective, that does not make much sense to know that the fifth test has completed whereas you are still processing the the third one; that would be quite a burden on the reporter to keep track of the state of all the tests for little benefit. It is much easier to read and process a linear sequence of messages (that is a simple It means that the reporting stream can wait for a test to complete before sending messages it might already be aware of, but that does not mean the tests are blocked. If you write the following program for example: concurrent tests exampleimport { test } from 'zora';
const wait = (time) =>
new Promise((resolve) => {
setTimeout(() => resolve(), time);
});
test('parent', async (t) => {
t.test('test 1', t => {
t.ok(true,'hello1');
})
t.test('test 2', async (t) => {
t.ok(true, 'hello 2');
await wait(2000);
});
t.test('test 3', async (t) => {
t.ok(true, 'hello 3');
await wait(3000);
});
t.test('test 4', async (t) => {
t.ok(true, 'hello 4');
await wait(2000);
});
}); You'll see the reporting stream prints the result fort test 1, then waits for 2 seconds before the test 2 complete but it will wait only 1 second (and not three) to report the third test afterward. The fourth test will then follow immediately
Not all the results are stuck. In your example, you'll still see the test report for the first assertion whereas the reporting stream is still waiting for the second one to complete. And If you write more assertions/sub tests which are faster than 2 seconds before that second test, you will see them as well The main advantage of async Iterable here is to write in a single imperative function all the lifecycles (started, ended, etc) and handle the recursivity for sub tests: Also, Async iterables play nicely with web and node streams while providing a much user friendly API (a whole transform stream is a simple map function): custom reporter example// stream-utils.js
export const map = (mapFn) => async function* (stream) {
for await (const streamElement of stream) {
yield mapFn(streamElement);
}
};
export const filter = (predicate) => async function* (stream) {
for await (const streamElement of stream) {
if (predicate(streamElement)) {
yield streamElement;
}
}
};
// reporter.js
import {diffString} from 'json-diff';
import {map, filter} from './stream-utils.js'
const keepFailingOnly = filter((message) => message?.data?.pass === false);
const diff = map(({data: {expected, actual, at}}) => ({
at,
diff: diffString(expected, actual)
}));
export const diffReporter = (opts = {}) => async (messageStream) => {
const sourceStream = diff(keepFailingOnly(messageStream));
for await (const {at, diff} of sourceStream) {
console.log(diff);
console.log(at);
}
};
// test.js
import {createHarness} from 'zora';
import {diffReporter} from './reporter.js'
const harness = createHarness();
harness.test('test 1', (t) => {
t.eq({foo: 'bar', woot: 'woot'}, {foo: 'baz', woot: 'woot'});
});
harness.report({reporter: diffReporter()}); And that's it you have a custom reporter ! |
Beta Was this translation helpful? Give feedback.
-
Just for completeness, here's the facility I came up with, which provides a synchronous (push) API and maps it to an async iterable: https://gist.github.com/mindplay-dk/76d07c2f065c73213556349363c8fb5d I don't think we have a use for this, just posting in case others or future self finds this and wonders how that was doable. 🙂 |
Beta Was this translation helpful? Give feedback.
-
I was under the impression that zora would actually stream results to a reporter as soon as possible.
That appears not to be the case?
In this example, starting
test 2
will block stream for 2 seconds - thehello 2
andhello 3
messages will be blocked by from reaching the reporter untiltest 2
has finished.Looking at the source-code, sure enough, every test will
await testRoutine
, effectively waiting for the entire test to complete, beforeyield* assertion
- running results get pushed to a list ofassertions
byonResult
, and are stuck there until the test finishes.Maybe it's by design. Maybe it doesn't matter. I'm not raising this as a matter of criticism, I'm just trying to understand more intricately how this works. I had assumed the use of
asyncIterator
was a means of streaming the results all the way from the front-end (tests) to the back-end (reporter).Is there any particular benefit to using
asyncIterator
in this way? I mean, is it any better or worse than simply returning aPromise
withassertions
? All of the results are essentially "stuck" inassertions
until the top-level test collects everything anyway - the only thing that gets through right away is the test description.But maybe that's all it was intended to do? 🙂
Beta Was this translation helpful? Give feedback.
All reactions