简体   繁体   中英

How to know if a function definition has been changed in NodeJS?

I'm trying to write a caching system for some time-intensive functions (Network requests/computation heavy) and I need to generate a fingerprint from the functions in order to invalidate the cached results once a developer changes the functions.

I have tried the following approach to generate the fingerprint:

const crypto = require('crypto')
const generateFingerprintOfFunc = (inputFunc) => {
  const cypher = crypto.createHash('sha256');

  cypher.update(inputFunc.toString());

  return cypher.digest('hex');
}

However, the problem with this approach is that the fingerprint won't be changed once the developer changes any of the functions that are called inside the function that is being fingerprinted because that function's definition hasn't really changed.

const foo = () => {
  return bar() + 1;
}

const bar = () => {
  return 1;
}


const fingerprintOfFoo = generateFingerprintOfFunc(foo); // 489290d22f653965a59e2e5fbb7b626535babd660f7f49501fc88c3e7fbc0176

Now I will change the bar function:

const foo = () => {
    return bar() + 1;
  }

const bar = () => {
    return 10;
}

const fingerprintOfFoo = generateFingerprintOfFunc(foo); // 489290d22f653965a59e2e5fbb7b626535babd660f7f49501fc88c3e7fbc0176

As you can see the fingerprint has not changed while the return value of the function has.

Why do I need to do this?

I'm trying to generate dynamic and automatic mocks for my expensive functions during development and testing. See this SO Question

I wanna know if there is a way that v8 would give me the path of the files making up a certain function and all of its internals.

No.

It can't. JavaScript is too dynamic.

Consider this example:

let helper;

const foo = () => helper() + 1;

const bar = () => 1;
const baz = () => 2;

helper = bar;

So far, this does the same as your example. Now suppose someone either changed the last line to read, or added a new line that reads helper = baz . In that case, no function's definition has changed, but foo 's behavior has, In fact: one can take that same idea to construct an even simpler case:

let global = 42;
const foo = () => global;

If someone changes global (whether statically in the source, or dynamically), foo 's return value will change, but no function definition will. In case of dynamic assignment, not even anything in the source will change. And of course such an assignment could depend on arbitrary conditions/circumstances, such as user interaction, time of day, Math.random() , whatever.

In general, the only way (for anyone, including the engine) to figure out what a function will return is to execute it.
Memoization works if developers carefully choose functions that lend themselves to memoization.
An automated system that takes any arbitrary function (without imposing any limitations on what the function does) and memoizes it, or determines whether it will the same value as last time, without actually executing it , is impossible to create in JavaScript.

What do you mean when you say "changes the function"? As in, you have jest in watch mode and want to only pick up changes when they make fundamental changes? I think the real question is why this is necessary, to begin with.

  1. How are you generating dynamic mocks? Does this imply that you repeatedly creating these mocks in real-time with network requests? Then they aren't really mocks. You are just making real requests and then deciding to temporarily store it somewhere. You could just skip that data-saving step and your resulting testing process would be identical. That's a glorified integration test

  2. I guess others can disagree, but your unit testing philosophy seems a little flawed. Dynamic mocks imply that you have no control over what situation ends up getting tested. If you are not in full control of your mocks, couldn't you end up testing the exact same case (or edge case) repeatedly? That's a waste of resources and can lead to flawed tests. Covering all of your intended cases would happen by coincidence, as opposed to explicit intent. Your unit tests should be deterministic. Not stochastic.

It seems like you need to solve your testing methodology, as opposed to accepting that your tests are slow and developing strategies on how to work around it.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM