A test in our frankly very complicated React app is taking ~1000 times longer to run than expected, and I'm trying to pin down where/why.
I started by manually calling console.time('name of test')
in the test file, and then manually dotting console.timeLog('name of test', 'did a thing')
around the application in all the places that were called and seemed likely to cause slow downs.
I noticed a lot of these places were inside React hooks - they aren't slow themselves, but were helping me see how long it took for coffee to get there.
I decided I needed to write a monkey patch in a Jest setupFilesAfterEnv
file for logging when React hooks callback are called, and what with (for this example, I'll use useEffect
)
const React = require('react');
let timeTestName = null;
let doTimeLog = false;
let prevUseEffect;
beforeAll(() => {
({ timeTestName } = global);
const prevUseEffect = React.useEffect;
React.useEffect = (cb, deps) => {
if(doTimeLog && timeTestName && Array.isArray(deps) && !__filename.includes('node_modules')){
console.timeLog(timeTestName, `Use Effect called with ${JSON.stringify(deps, null, 2)}`): // log useEffect being called with timer
}
prevUseEffect(cb, deps);
}
});
beforeEach(() => {
const { testPath } = expect.getState();
if(testPath.endsWith(`${timeTestName}.test.js`)) {
doTimeLog = true;
console.time(timeTestName); // start timer
} else {
doTimerLog = false;
}
});
afterEach(() => {
doTimerLog = false;
console.log(testToTimeName); // end timer
});
afterAll(() => {
React.useEffect = prevUseEffect;
})
However what I really want as well is the variable names in the dependency list, which I cannot get (at least not without changing non-test code).
One thought I had was using a Jest transformer to make all the arrays into objects so I preserve the variable names as keys; something like:
module.exports = {
process(sourceText) {
return {
code: `convertSquareToCurly(sourceText)`,
};
},
};
module.exports = {
transform: {
'matchDepList':
'<rootDir>/deplistTransformer.js',
},
};
So during tests only my useEffects become:
useEffect(() => foo(a, b, c), { a, b, c})
(I will handle this new object in my monkey patch)
I'm reasonably confident I can make the above work.
Then I in my monkey patch I can call console.log(Object.entries(deps));
and prevUseEffect(cb, Object.values(deps));
.
However if I'm concerned that calling Object.values
will cause the hook's callback to always be called.
I haven't been able to try the transformer yet, but I don't want to waste time writing it if passing Object.values
won't work in place of the untransformed dependency list.
Is there another way to monkey patch and get variable names, or would this work?