I want my test suite to work as if it was on Windows platform. Unfortunately, a lot of dependencies use path module. I want to mock it with path.win32 implementation. However, this approach doesn't work:
const winPath = require("path").win32;
jest.mock("path", () => winPath);
ReferenceError: Cannot access 'winPath' before initialization
What is the proper way to do this?
jest.mock() should work. The below example shows how to mock the win32.normalize() method.
const winPath = require("path").win32;
jest.mock('path', () => {
return {
...(jest.requireActual('path') as typeof import('path')),
win32: {
normalize: jest.fn(),
}
}
})
describe('74717157', () => {
test('should pass', () => {
winPath.normalize.mockImplementation(() => 'override the original implementation')
expect(jest.isMockFunction(winPath.normalize)).toBeTruthy();
expect(winPath.normalize()).toBe('override the original implementation')
})
})
Test result:
PASS stackoverflow/74717157/index.test.ts (10.557 s)
74717157
✓ should pass (1 ms)
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 14.116 s
Related
I have 2 files with 1 function in it. My test is just as simple, see below:
doc.js
export function createSynthId(doc) {
const synthId = `${doc.attachmentId}-${doc.materialId}-${doc.fileName}-${doc.title}-${doc.materialURL}`;
return synthId;
}
doc.test.js
import { createSynthId } from './doc';
describe('doc', () => {
it('create synthetic id', () => {
expect(createSynthId(global.doc)).toEqual('987654-123456-File Name.pptx-undefined-');
});
it('create synthetic id', () => {
expect(createSynthId({})).toEqual('undefined-undefined-undefined-undefined-undefined');
});
});
My second file is virtually the same, just a larger function. Both tests pass, but coverage is being reported at 0% for Statements, Functions, and Lines, but 100% for Branches. The coverage report is showing all the lines red as well.
We have many similar files and they all work. What am I missing here?
UPDATE
I added a temp function to doc.js
export const makeTestsHappy = () => {
console.log(CONFIG);
};
adding to doc.test.js
it('does more', () => {
makeTestsHappy();
});
and when I try to test that, I get the error TypeError: (0 , _doc.makeTestsHappy) is not a function
Dummy over here was mocking the file I was testing. I forgot to jest.unmock('/doc') It immediately started working when I unlocked. Thanks all for your patience :D
Try to rename your test file from doc.test.js to doc.spec.js. You are using the BDD syntax and the correct name should include spec.
I am new to jest unit testing as well as NodeJS and i'm trying to test my API using the following code.
const request = require('supertest');
const quick_registration = require('../../../routes/register/quick_registration_of_user')
describe('Quick Registration of users', () => {
beforeAll(() => {
con.connect();
});
describe("GET / ", () => {
test("It should respond with an array of students", async () => {
const response = await request(quick_registration).get("/api/quickregistration");
expect(response.status).toBe(200);
});
});
})
But the test keep failing and throws me an error
FAIL tests/routes/register/quick_registration_of_user.test.js
Quick Registration of users
GET /
× It should respond with an array of students (20ms)
● Quick Registration of users › GET / › It should respond with an array of students
TypeError: Cannot read property 'apply' of undefined
at node_modules/express/lib/router/index.js:635:15
at next (node_modules/express/lib/router/index.js:260:14)
at Function.handle (node_modules/express/lib/router/index.js:174:3)
at Server.router (node_modules/express/lib/router/index.js:47:12)
Test Suites: 1 failed, 1 total
Tests: 1 failed, 1 total
Snapshots: 0 total
Time: 1.649s, estimated 2s
Ran all test suites.
Jest did not exit one second after the test run has completed.
This usually means that there are asynchronous operations that weren't stopped in your tests. Consider running Jest with `--detectOpenHandles` to troubleshoot this issue.
I'm assuming the line
const quick_registration = require('../../../routes/register/quick_registration_of_user')
is requiring the file where you've defined your router. You need to be requiring instead the file where you define your app.
I've been playing around comparing the functional paradigm and the object-oriented paradigm.
As part of this - I'm wanting to do some performance tests.
I have some tests that look like this for now:
it("Some long running performance test", () => {
const result = myFunctionWithLotsOfData();
});
For now, I'm just printing how long this code takes to run (around 5000ms).
I like using Jest for all of the assertions and mocking functionality it gives, and it's live reload, etc.
However, I don't want these tests to all the time, I'd run create a npm script like npm test:performance, and only run these tests if an environment variable is present or similar.
What's the best way to do this?
const itif = (condition) => condition ? it : it.skip;
describe('suite name', () => {
itif(true)('test name', async () => {
// Your test
});
});
Along the same lines as the accepted answer:
const maybe = process.env.JEST_ALLOW_INTEG ? describe : describe.skip;
maybe('Integration', () => {
test('some integration test', async () => {
expect(1).toEqual(1);
return;
});
});
A small variation on the accepted post, but if we combine Jest's test.skip(...) with the kind of blind argument forwarding that modern JS allows thanks to the spread operator, we can get a cleaner solution that conditionally runs tests, while letting Jest know it skipped something "the official way", without needing a "functionfunction" call:
const testIf = (condition, ...args) =>
condition ? test(...args) : test.skip(...args);
describe(`a mix of tests and conditional tests`, () => {
test(`this will always run`, () => {
expect("1").toBe("1");
});
testIf(Math.random() > 0.5, `this will only run half the time`, () => {
expect("2").toBe("2");
});
});
or here's the typescript version
const testIf = (condition: boolean, ...args: Parameters<typeof test>) =>
condition ? test(...args) : test.skip(...args);
Half the time this will run as:
Test Suites: 1 passed, 1 total
Tests: 2 passed, 2 total
Snapshots: 0 total
Time: 0.451 s, estimated 1 s
And half the time it will show:
Test Suites: 1 passed, 1 total
Tests: 1 skipped, 1 passed, 2 total
Snapshots: 0 total
Time: 0.354 s, estimated 1 s
But to cover the part that no one seems to have included in their answers, we can use this to skip over long-running tests by combining it with "looking at process.argv for a runtime flag":
const runLong = process.argv.includes(`--run-long`);
const testIf = (condition, ...args) =>
condition ? test(...args) : test.skip(...args);
describe(`Run tests, and long tests only if told to`, () => {
// ...
testIf(runLong, `Some long-running test, skip if --run-long is not set`, () => {
// a test that runs for a long time here
});
});
And then we can just put that runtime flag in our npm script. However, we need to make sure to forward that flag on to our script, rather than to jest, or to npm:
...
"scripts": {
...
"test": "jest somedir",
"test:long": "npm run test -- -- --run-long",
...
},
...
This looks kind of weird, and it is kind of weird, but it's a consequence of how argument forwarding works for npm scripts:
The first -- tells npm that it needs to forward what follows, rather than interpreting that flag itself (in effect: this makes npm run jest somedir -- --run-long).
The second -- tells jest that it needs to forward what follows, instead of considering it a runtime flag for itself, so that our script gets to see it in its process.argv list (so that we call ourscript --run-long).
A common mistake is to forget that second --, which would lead to a fun error that doesn't tell you that just forgot two dashes, and no tests running at all.
Here is one solution, create itif function so that we can run the unit tests based on some conditions.
For example, the itif function:
export const itif = (name: string, condition: () => boolean | Promise<boolean>, cb) => {
it(name, async done => {
if (await condition()) {
cb(done);
} else {
console.warn(`[skipped]: ${name}`);
done();
}
});
};
The unit tests:
describe('test suites', () => {
itif(
'functional-approach-2 perforance test',
async () => process.env.PERFORMANCE_TEST === 'true',
done => {
console.info('Functional Approach 2 Performance Test');
const t0 = Date.now();
const m0 = getMemory();
const li0 = instantiateFanRecursive(20, 2, 0, 0, 1, 1, 2, 1);
const r0 = getDrawablesFromLineInstances(li0);
printMemory(getMemory() - m0);
console.info(`Length: ${r0.length}`);
console.info(`Time Taken: ${Date.now() - t0}ms`);
done();
}
);
});
Run your unit test when the value of process.env.PERFORMANCE_TEST environment variable equal 'true', the result:
PERFORMANCE_TEST=true npm t -- /Users/elsa/workspace/github.com/mrdulin/jest-codelab/src/stackoverflow/58264344/index.spec.t
> jest-codelab#1.0.0 test /Users/elsa/workspace/github.com/mrdulin/jest-codelab
> jest --detectOpenHandles "/Users/elsa/workspace/github.com/mrdulin/jest-codelab/src/stackoverflow/58264344/index.spec.ts"
PASS src/stackoverflow/58264344/index.spec.ts
test suites
✓ functional-approach-2 perforance test (18ms)
console.info src/stackoverflow/58264344/index.spec.ts:22
Functional Approach 2 Performance Test
console.log src/stackoverflow/58264344/index.spec.ts:4
0
console.info src/stackoverflow/58264344/index.spec.ts:28
Length: 0
console.info src/stackoverflow/58264344/index.spec.ts:29
Time Taken: 5ms
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 5.67s, estimated 9s
Do not run the unit test when the value of process.env.PERFORMANCE_TEST environment variable is not set:
npm t -- /Users/elsa/workspace/github.com/mrdulin/jest-codelab/src/stackoverflow/58264344/index.spec.ts
> jest-codelab#1.0.0 test /Users/elsa/workspace/github.com/mrdulin/jest-codelab
> jest --detectOpenHandles "/Users/elsa/workspace/github.com/mrdulin/jest-codelab/src/stackoverflow/58264344/index.spec.ts"
PASS src/stackoverflow/58264344/index.spec.ts
test suites
✓ functional-approach-2 perforance test (11ms)
console.warn src/stackoverflow/58264344/index.spec.ts:11
[skipped]: functional-approach-2 perforance test
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 2.758s, estimated 5s
add.js
export default a => b => a+b;
module.js
import add from './add';
export default {
add1: n => add(1)(n),
};
test/module.js
import add from '../add';
import module from '../module';
jest.mock('../add', () => () => jest.fn());
module.add1(6);
expect(add.mock.calls).toHaveLength(1);
this can be called, but add wouldn't be a mock function, instead add() is a mock function, but the call params were not recorded correctly.
jest.mock('../add', () => () => jest.fn(a => b => a+b));
has also tried this, which doesn't seem to work correctly as well.
jest.mock('../add', jest.fn(a => b => a+b));
this would throw the inline function error
Is there a correct way to mock curry function at the moment?
Simple version should look like this
jest.mock('../add', () => (a) => jest.fn(b => a+b));
So you mock add module with a function that return that when gets called it returns the spy, problem you can't test anything on the spy.
So we need to refactor it so you have the add1 think as a spy in scope of the test
import add from '../add'
jest.mock('../add', () => jest.fn)
const addC = jest.fn()
add.mockImplemetation((a) => {
addC.mockImplementation((b => a+b)
return addC
})
I know this is a bit old but with the following answer I would have saved so much time...
import module from "./module";
const mockOperation = {
add: jest.fn()
};
jest.mock("./add", () => () => {
return mockOperation.add;
});
describe("add ", () => {
it("is called at least once", () => {
module.add1(6);
expect(mockOperation.add.mock.calls).toHaveLength(1);
});
});
A very importante thing: the constant function that you want to mock must start with the word mock. Otherwise it will throw an error.
const mockOperation = {
add: jest.fn()
};
With the mockOperation constant assigned to the add file, by reference is calling to it's add method which is a jest.fn()
I've had to do this a lot recently so I wrote a helper function to help me out.
curryMock = (baseFn, maxCurries) => {
var callIndex = 0
var curries = []
let returnFn
baseFn.mockImplementation((arg) => {
curries[callIndex] = 0
const fn = returnFn(callIndex)
callIndex++
return fn
})
returnFn = (index: number) => {
if (curries[index] < maxCurries) {
curries[index]++
return (arg: any) => {
baseFn.mock.calls[index].push(arg)
return returnFn(index)
}
}
}
}
You pass the function a jest.fn() and curryMock modifies it's implementation so that all the arguments return functions are called with will be added to the original jest.fn().mock.calls array. The second argument specifies how many times to return a function. maxCurries = 0 is for a regular function () => {},
maxCurries = 1is for a regular function() => => {}`, etc.
In your case:
import add from '../add';
import module from '../module';
jest.mock('../add');
curryMock(add)
module.add1(6);
expect(add).toHaveBeenCalledWith(1, 6);
//equivalent to
//expect(add).toHaveBeenNthCalledWith(1, 1, 6);
//if you others to test..
module.add7(4)
expect(add).toHaveBeenNthCalledWith(2, 7, 4)
curryMock is a quick utility function that I wrote for personal use. I will likely clean it up and extend it later, but I hope you get the idea: testing curried functions does not have to laborious.
I want run a bunch of tests on the results obtained from a promise call. I know it can be done as:
it ('should pass test foo1', () => {
return promisecall()
.then(result => {
expect(result.length).to.equal(42)
expect(some other test on data)
expect(some other test on data)
.
.
})
})
According to my understanding, what this does is:
Runs a single test (i.e should pass test foo1) which passes only if all the expect conditions are true, and these expect parts will not show in the output screen.
How can I add multiple 'it' / unit tests on the results of a single promise result, so that the different test cases actually show up in the output?
You should be able to group them together in a describe() and run the promise in a before():
describe('things with promises', () => {
var promiseResult;
before(() => promiseCall().then(result => promiseResult = result));
it ('should pass test foo1', () => expect(promiseResult.length).to.equal(42));
it ('should pass test foo2', () => expect(some other test));
// ...
});