Giter VIP home page Giter VIP logo

Comments (7)

lukeapage avatar lukeapage commented on July 16, 2024 1

I have some experience with the testing of complex async projects. If you can tell me what's the trouble that makes you want to choose sync, I may help you to find a better way to test, other than hacking a primary lib.

A couple of years ago I had alot of problems with async tests where things finishing in different orders resulted in different results and then inconsistent tests. In those cases there were multiple setTimeout's with different times and not only was it inconsistent but slow (I realise that promises in particular don't suffer from long timeouts.. but if you start making calls sync it tends to affect everything).

So I prefer to mock out date, mock out timers like setTimeout and flush promises. It means you can have full control on the execution order and make one test in one order and one in another.

I wouldn't want to change nextTick to just be sync because it changes the behaviour e.g.

let a;
Promise.reject().catch(() => a());
a = () => console.log('only works async');

but what I do prefer to do is to treat my unit test like its a top level environment - e.g. once user code has returned to the test, if I run pending timers / flush promises then, I keep almost the same behaviour as a live environment, but control execution from my tests.

but your right, I have managed to implement this using nextTick:

// replace Promise with one with a Promise.flush function to make promises sync
import Yaku from 'yaku';

const queue = [];
let timeoutid = null;

function runQueue() {
    const q = queue.slice(0);
    queue.length = 0;
    for (let i = 0; i < q.length; i++) {
        q[i]();
    }
}

// Promises should run as normal but be able to be flushed
// as if the test ended and the setImmediate began
Yaku.nextTick = (fn) => {
    queue.push(fn);
    if (!timeoutid) {
        timeoutid = global.setImmediate(Yaku.flush);
    }
};

Yaku.flush = () => {
    while (queue.length) {
        runQueue();
    }
    global.clearImmediate(timeoutid);
    timeoutid = null;
};

Yaku.resolvedValue = (promise) => {
    let value;
    promise.then((resolvedValue) => {
        value = resolvedValue;
    });
    Yaku.flush();
    return value;
};

Yaku.isFulfilled = (promise) => {
    let isFulfilled = false;
    promise.then(function() {
        isFulfilled = true;
    });
    Yaku.flush();
    return isFulfilled;
};

Yaku.isRejected = (promise) => {
    let isRejected = false;
    promise.catch(function() {
        isRejected = true;
    });
    Yaku.flush();
    return isRejected;
};

// stop unhandled exceptions on Promise.reject which occurs often in the testing code
const originalPromiseReject = Yaku.reject;
Yaku.reject = (value) => {
    const promise = originalPromiseReject.call(Yaku, value);
    promise.catch(() => {});
    return promise;
};

export const isRejected = Yaku.isRejected;
export const isFulfilled = Yaku.isFulfilled;
export const resolvedValue = Yaku.resolvedValue;
export const flush = Yaku.flush;

global.Promise = Yaku;

and I am a bit more on the fence about this than I used to be - I test with jest and it does have pretty good support for promises, so I could probably keep promises async and use async tests, then use fake timers only to skip waiting for any setTimeouts.

btw in case your wondering why I mention setTimeouts so often - that is to mock rest requests and websocket data that come in at different times and orders.

from yaku.

ysmood avatar ysmood commented on July 16, 2024

Actually, there's an easier approach, have you read this section of the doc?

https://github.com/ysmood/yaku#yakunexttick?

image

I'm not sure if this is what you want, but if you want to resolve promise synchronously without any side effect it should work for you.

from yaku.

ysmood avatar ysmood commented on July 16, 2024

BTW, it's better to use the async resolution which will enforce you to think asynchronously and reduce some production bugs that caused by lacking asynchronous planning.

I have some experience with the testing of complex async projects. If you can tell me what's the trouble that makes you want to choose sync, I may help you to find a better way to test, other than hacking a primary lib.

from yaku.

lukeapage avatar lukeapage commented on July 16, 2024

thats a good point. I could try implementing a scheduler on top of this, which records setTimeout, then on flush, clears it and runs it immediately.

from yaku.

ysmood avatar ysmood commented on July 16, 2024

Actually what you want to do is have full control of the IO, I think you already know pure-function, for Yaku the only input IO to the outside world is nextTick, so if you take control of it you can make Yaku a pure module without any IO side effect.

If you want to go extremely, you'd better also mock all other common IOs, including File, Net, and Timer, then you even don't have to touch Yaku, you'll get what you want automatically because Yaku uses Timer internally.

If your system is fully inside a sandbox without any IO to outside world, you will get the best performance of a single CPU core for your tests. Normally people don't do that because IO is not always the bottleneck of the project, they prefer to put the time to more important problems.

If you want to leverage multiple CPU cores without all those common IOs, you might need to create a new test framework other than something like ava. Actually, I'm interested to create a perfect IO sandbox test framework for multiple CPU cores.

from yaku.

ysmood avatar ysmood commented on July 16, 2024

// stop unhandled exceptions on Promise.reject which occurs often in the testing code
const originalPromiseReject = Yaku.reject;
Yaku.reject = (value) => {
const promise = originalPromiseReject.call(Yaku, value);
promise.catch(() => {});
return promise;
};

About this code, I recommend you to read the doc of this section:

https://github.com/ysmood/yaku#yakuunhandledrejectionreason-p

nextTick is the only input IO of Yaku,
unhandledRejection is the only output IO of Yaku, so you can use it to mute Yaku

from yaku.

ysmood avatar ysmood commented on July 16, 2024

Close because of no activity

from yaku.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.