Faker.js allow you to easily create faked data using for example the following:
import * as faker from 'faker'
console.log(faker.lorem.text())
So I tried to mock this library to spy the use of faker.lorem.text():
import * as faker from 'faker'
const mockFakerLoremText = jest.fn()
jest.mock('faker', () => ({
lorem: {
text: mockFakerLoremText
}
}))
it('should have called lorem.text() method', () => {
faker.lorem.text()
expect(mockFakerLoremText).toHaveBeenCalledTimes(1)
})
But then I got the following error:
ReferenceError: Cannot access 'mockFakerLoremText' before initialization
So has someone an idea how I can spy on the call of this method .lorem.text()?
From the docs Calling jest.mock() with the module factory parameter
A limitation with the factory parameter is that, since calls to jest.mock() are hoisted to the top of the file, it's not possible to first define a variable and then use it in the factory
That's why you got the error.
An working example using "jest": "^26.6.3":
index.test.js:
import * as faker from 'faker';
jest.mock('faker', () => ({
lorem: {
text: jest.fn(),
},
}));
it('should have called lorem.text() method', () => {
faker.lorem.text();
expect(faker.lorem.text).toHaveBeenCalledTimes(1);
});
unit test result:
PASS examples/65924623/index.test.ts
√ should have called lorem.text() method (3 ms)
----------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
----------|---------|----------|---------|---------|-------------------
All files | 0 | 0 | 0 | 0 |
----------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 6.913 s, estimated 7 s
Related
I am trying to write a test for appendfilesync. I call this when using a logger and so I have 1 line of code not covered. Here is the code (note: using tslog for logger)
export function logToTransport(logObject: ILogObject) {
appendFileSync('monopoly_deal.log', JSON.stringify(logObject) + '\n');
}
I've tried mocking up 'fs' which can work so that nothing actually writes to file but I don't actually test the writing. Here is start of my test code to set things up:
const log: Logger = new Logger({ name: 'card_types_test' });
log.attachTransport(
{
silly: CardTypes.logToTransport,
debug: CardTypes.logToTransport,
trace: CardTypes.logToTransport,
info: CardTypes.logToTransport,
warn: CardTypes.logToTransport,
error: CardTypes.logToTransport,
fatal: CardTypes.logToTransport,
},
'info',
);
// Here I would need to setup a jest.spyon I think to intercept the call but not sure how to do it. Something like const spy = jest.fn(CardTypes.logToTransport);
log.info('test'); // Logs to file
expect(spy).toequal({object with my data}); // I didn't put what object would actually look like for brevity.
Any guidance on how to mock up writing to files is greatly appreciated along with any other critiques (still a junior level programmer at best).
You should test the code logic of logToTransport function rather than fs.appendFileSync method. fs.appendFileSync is a built-in method of Node.js and it's well tested.
You can only mock the fs.appendFileSync method, this way is called mocking partials. We are going to test the implementation detail of logToTransport function, this test strategy is called white-box testing.
Besides, we can use jest.isMockFunction to verify if we do the mocking partials successfully.
E.g.
index.ts:
import { appendFileSync } from 'fs';
type ILogObject = any;
export function logToTransport(logObject: ILogObject) {
appendFileSync('monopoly_deal.log', JSON.stringify(logObject) + '\n');
}
index.test.ts:
import fs from 'fs';
import { logToTransport } from '.';
jest.mock('fs', () => ({
...(jest.requireActual('fs') as typeof fs),
appendFileSync: jest.fn(),
}));
describe('73466276', () => {
test('should pass', () => {
expect(jest.isMockFunction(fs.appendFileSync)).toBeTruthy();
expect(jest.isMockFunction(fs.appendFile)).toBeFalsy();
logToTransport({ level: 'debug', payload: { name: 'teresa teng' } });
expect(fs.appendFileSync).toBeCalledWith(
'monopoly_deal.log',
JSON.stringify({ level: 'debug', payload: { name: 'teresa teng' } }) + '\n'
);
});
});
Test result:
PASS stackoverflow/73466276/index.test.ts (11.306 s)
73466276
✓ should pass (3 ms)
----------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
----------|---------|----------|---------|---------|-------------------
All files | 100 | 100 | 100 | 100 |
index.ts | 100 | 100 | 100 | 100 |
----------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 12.012 s
I have an app that's making heavy use of Hapi's Server Methods. The methods are being applied via an exported register function, and the code that's being executed for the method is in the same file and thus not exported. I'm trying to write tests for these methods without exporting the functions that they call in the simplest possible way, but I haven't found any examples of how to do so.
export function register(server) {
server.method(
'methodNameA',
async (path, {}) => {
// Some code here
return something; // I want to test this result
},
{
cache: methodCache.halfDay,
generateKey(path, { ... } = {}) {
return something;
},
}
);
};
Abstracting that logic is an option, but I'd rather not expose it just for a test. I'd also prefer to not test an entire route just to validate this bit of logic (though that may be the ultimate solution here).
I will use jestjs as my unit testing framework. You can provide a mocked server and a mocked implementation for server.method(). Then you can get the original method in your test case.
After getting the original method, test it as usual.
E.g.
register.ts:
export function register(server) {
server.method('methodNameA', async () => {
return 'something';
});
}
register.test.ts:
import { register } from './register';
describe('67093784', () => {
it('should pass', async () => {
let methodNameA;
const mServer = {
method: function mockImplementation(m, func) {
methodNameA = func;
},
};
register(mServer);
// test method
const actual = await methodNameA();
expect(actual).toEqual('something');
});
});
unit test result:
PASS examples/67093784/register.test.ts (11.981 s)
67093784
✓ should pass (3 ms)
-------------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
-------------|---------|----------|---------|---------|-------------------
All files | 100 | 100 | 100 | 100 |
register.ts | 100 | 100 | 100 | 100 |
-------------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 14.766 s
Minimal, reproducible example
SomeClass.js
import Rx from "rxjs/Rx.js";
class SomeClass {
constructor() {
this.subject = new Rx.Subject();
}
}
SomeClass.test.js
import SomeClass from "./SomeClass.js";
let someClass = new SomeClass();
After running npm test, I get an error saying TypeError: Cannot read property 'Subject' of undefined. Rxjs has been installed in my project.
Any ideas?
Option 1. use Manual Mocks, mock rxjs/Rx.js module explicitly.
E.g.
SomeClass.js:
import Rx from 'rxjs/Rx.js';
export default class SomeClass {
constructor() {
this.subject = new Rx.Subject();
}
}
SomeClass.test.js:
import SomeClass from './SomeClass';
import Rx from 'rxjs/Rx.js';
jest.mock('rxjs/Rx.js', () => {
return { Subject: jest.fn() };
});
describe('65261235', () => {
it('should pass', () => {
let someClass = new SomeClass();
expect(Rx.Subject).toBeCalledTimes(1);
});
});
unit test result:
PASS examples/65261235/SomeClass.test.js
65261235
✓ should pass (2 ms)
--------------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
--------------|---------|----------|---------|---------|-------------------
All files | 100 | 100 | 100 | 100 |
SomeClass.js | 100 | 100 | 100 | 100 |
--------------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 5.877 s
Option 2. enable automock for jestjs.
This option tells Jest that all imported modules in your tests should be mocked automatically. All modules used in your tests will have a replacement implementation, keeping the API surface.
I have a function, lets call it generateName, which as you’ve guessed it, generates a name. The problem is that a new name is generated each time time a test is ran.
In one of my tests, I assert that a function is called with an object containing this name. However, the name keeps on changing. I could just check that the object has property name, but I don’t really want to do that.
My idea is that I can mock the return value of the generateName function and do something like this
Import { generateName } from ‘libs/generateName’
jest.fn(generateName).mockResolvedValue ( ‘hello’ )
expect ( spy ).toHaveBeenCalledWith (
expect.objectContaining ( {
name: 'houses',
} )
)
You can use jest.mock(moduleName, factory, options) to mock libs/generateName module.
E.g.
generateName.ts:
export async function generateName() {
const name = Math.random() + '';
return name;
}
main.ts:
import { generateName } from './generateName';
export function main() {
return generateName();
}
main.test.ts:
import { main } from './main';
import { generateName } from './generateName';
jest.mock('./generateName', () => {
return {
generateName: jest.fn(),
};
});
describe('61350152', () => {
it('should pass', async () => {
(generateName as jest.MockedFunction<typeof generateName>).mockResolvedValueOnce('hello');
const actual = await main();
expect(actual).toBe('hello');
});
});
unit test results with coverage report:
PASS stackoverflow/61350152/main.test.ts (28.524s)
61350152
✓ should pass (6ms)
----------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
----------|---------|----------|---------|---------|-------------------
All files | 100 | 100 | 100 | 100 |
main.ts | 100 | 100 | 100 | 100 |
----------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 31.98s
I am unable to mock 3rd party function call in typescript.
The third party library is moment-timezone and I want to mock the code to get browser timezone to write jest test.
Below is the code I need to mock and return string as 'Australia/Sydney'
moment.tz.guess()
I am trying to use jest.mock() as :-
jest.mock('moment-timezone', () => () => ({ guess: () => 'Australia/Sydney' }));
Here is the solution:
index.ts:
import moment from 'moment-timezone';
export function main() {
return moment.tz.guess();
}
index.spec.ts:
import { main } from './';
import moment from 'moment-timezone';
jest.mock('moment-timezone', () => {
const mTz = {
guess: jest.fn()
};
return {
tz: mTz
};
});
describe('main', () => {
test('should mock guess method', () => {
(moment.tz.guess as jest.MockedFunction<typeof moment.tz.guess>).mockReturnValueOnce('Australia/Sydney');
const actualValue = main();
expect(jest.isMockFunction(moment.tz.guess)).toBeTruthy();
expect(actualValue).toBe('Australia/Sydney');
expect(moment.tz.guess).toBeCalled();
});
});
Unit test result with 100% coverage:
PASS src/stackoverflow/58548563/index.spec.ts
main
✓ should mock guess method (6ms)
----------|----------|----------|----------|----------|-------------------|
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
----------|----------|----------|----------|----------|-------------------|
All files | 100 | 100 | 100 | 100 | |
index.ts | 100 | 100 | 100 | 100 | |
----------|----------|----------|----------|----------|-------------------|
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 5.828s, estimated 19s