When testing a class via jest, does it handle imports? - javascript

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.

Related

Mock and asset a function from an external library

I'm struggling to figure out how to do this.
example.js
import Logger from "logging-library";
export default function example() {
Logger.error(new Error("Example Error")):
}
example.test.js
test("will log an error", () => {
expect(Logger.error).toHaveBeenCalledWith(new Error("Example Error");
});
The examples I've found might cover mocking an entire library, but don't seem to cover mocking and also asserting how it was called.
unit test solution:
example.js:
import Logger from 'logging-library';
export default function example() {
Logger.error(new Error('Example Error'));
}
example.test.js:
import Logger from 'logging-library';
import example from './example';
jest.mock(
'logging-library',
() => {
return { error: jest.fn() };
},
{ virtual: true },
);
describe('64858662', () => {
afterAll(() => {
jest.resetAllMocks();
});
test('will log an error', () => {
example();
expect(Logger.error).toHaveBeenCalledWith(new Error('Example Error'));
});
});
unit test result:
PASS src/stackoverflow/64858662/example.test.js
64858662
✓ will log an error (5ms)
------------|----------|----------|----------|----------|-------------------|
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
------------|----------|----------|----------|----------|-------------------|
All files | 100 | 100 | 100 | 100 | |
example.js | 100 | 100 | 100 | 100 | |
------------|----------|----------|----------|----------|-------------------|
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 4.373s, estimated 12s

can I mock an imported function in jest unit

I have a code like this
import fun from '../../../example';
export async function init (props:any){
if (fun()){
doSomething();
}
}
I'm creating unit tests for this code above, but I actually want to mock the implementation of fun in the file only as I can't alter fun in its original file
You can use jest.mock(moduleName, factory, options) to mock ../../../example module.
E.g.
index.ts:
import fun from './example';
export async function init(props: any) {
if (fun()) {
console.log('doSomething');
}
}
example.ts:
export default function fun() {
console.log('real implementation');
return false;
}
index.test.ts:
import { init } from './';
import fun from './example';
import { mocked } from 'ts-jest/utils';
jest.mock('./example', () => jest.fn());
describe('63166775', () => {
it('should pass', async () => {
expect(jest.isMockFunction(fun)).toBeTruthy();
const logSpy = jest.spyOn(console, 'log');
mocked(fun).mockReturnValueOnce(true);
await init({});
expect(logSpy).toBeCalledWith('doSomething');
expect(fun).toBeCalledTimes(1);
logSpy.mockRestore();
});
});
unit test result:
PASS stackoverflow/63166775/index.test.ts (13.298s)
63166775
✓ should pass (33ms)
console.log
doSomething
at CustomConsole.<anonymous> (node_modules/jest-environment-enzyme/node_modules/jest-mock/build/index.js:866:25)
----------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
----------|---------|----------|---------|---------|-------------------
All files | 100 | 50 | 100 | 100 |
index.ts | 100 | 50 | 100 | 100 | 4
----------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 15.261s

Mocking ES6 Class Modules exported as objects

Considering the example below
//AuthenticatorService.js
const UserService = require("./UserService");
class AuthenticatorService{
constructor(){
//Some initialisation
}
//Some methods
}
module.exports = new AuthenticatorService();
//UserService.js
class UserService{
constructor(){
//Again some initialisations
}
}
module.exports = new UserService();
So when I try to mock the UserService class in my AuthenticatorService.spec.js file the constructor of the UserService is getting executed as it is being exported in that way. But I don't want that to be executed. Is it possible to mock the UserService module inside AuthenticatorService.spec.js file without invoking its constructor.
You could use jest.mock(moduleName, factory, options) to do this.
E.g.
UserService.js:
class UserService {
constructor() {
console.log('initialize UserService');
}
hello() {
console.log('user hello real implementation');
}
}
module.exports = new UserService();
AuthenticatorService.js:
const userService = require('./UserService');
class AuthenticatorService {
constructor() {
console.log('initialize AuthenticatorService');
}
hello() {
userService.hello();
}
}
module.exports = new AuthenticatorService();
AuthenticatorService.test.js:
const authenticatorService = require('./AuthenticatorService');
const userService = require('./UserService');
jest.mock('./UserService', () => {
return { hello: jest.fn() };
});
describe('62967707', () => {
afterAll(() => {
jest.resetAllMocks();
});
it('should pass', () => {
userService.hello.mockImplementationOnce(() => console.log('user hello mocked implementation'));
authenticatorService.hello();
expect(userService.hello).toBeCalledTimes(1);
});
});
unit test result:
PASS stackoverflow/62967707/AuthenticatorService.test.js (12.636s)
62967707
✓ should pass (11ms)
console.log
initialize AuthenticatorService
at new AuthenticatorService (stackoverflow/62967707/AuthenticatorService.js:5:13)
console.log
user hello mocked implementation
at Object.<anonymous> (stackoverflow/62967707/AuthenticatorService.test.js:13:60)
-------------------------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
-------------------------|---------|----------|---------|---------|-------------------
All files | 100 | 100 | 100 | 100 |
AuthenticatorService.js | 100 | 100 | 100 | 100 |
-------------------------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 14.703s
As you can see in the logs, the real constructor of UserService will not execute. And we mocked hello method of the UserService class's instance.

Mock the return value of an imported function in Typescript with Jest

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

How to mock a library function call in typescript?

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

Categories

Resources