Jest - mock a property and function from moment-timezone - javascript

Im trying to mock property tz and a function using jest but i dont know to mock both things together:
If run something like:
jest.mock('moment-timezone', () => () => ({weekday: () => 5}))
jest.mock('moment-timezone', () => {
return {
tz: {
}
}
})
I can mock attribute tz or instruction moment(). How can i write a mock for cover this code?
const moment = require('moment-timezone')
module.exports.send = () => {
const now = moment()
moment.tz.setDefault('America/Sao_Paulo')
return now.weekday()
}
Thanks

You could take advantage of the second parameter of jest.mock(), which would let you specify a custom implementation of the mocked module to use in testing.
Inside this custom implementation, you can also define some convenience helpers to emulate expected implementation values (e.g. weekday()).
// send-module.test.js
jest.mock('moment-timezone', () => {
let weekday
const moment = jest.fn(() => {
return {
weekday: jest.fn(() => weekday),
}
})
moment.tz = {
setDefault: jest.fn(),
}
// Helper for tests to set expected weekday value
moment.__setWeekday = (value) => weekday = value
return moment;
})
const sendModule = require('./send-module')
test('test', () => {
require('moment-timezone').__setWeekday(3)
expect(sendModule.send()).toBe(3)
})
Do note that manually providing the mock per test file can get tedious and repetitive if the module being mocked has a huge API surface. To address the latter case, you can consider authoring some manual mocks to make them reusable (i.e. using the __mocks__ directory convention) and supplement that by using jest.genMockFromModule().
The Jest documentation has some guidance about this.

Related

jest testing function call inside another function

Is it possible to test startServer() call once launcApp is called?
import { Server } from './Server/Server';
export class Launcher {
private server: Server;
public constructor() {
this.server = new Server();
}
public launchApp() {
this.server.startServer();
}
}
I tried to do
Server.prototype.startServer = jest.fn();
new Launcher.launchApp();
expect(Server.prototype.startServer).toBeCalled();
but it fails
Your approach worked for me. The only problem with your snippet was that you are not instantiating your Launcher properly. It needs to be:
Server.prototype.startServer = jest.fn();
new Launcher(). launchApp();
expect(Server.prototype.startServer).toBeCalled();
But if you are looking for other ways to mock methods in classes, there are many to achieve it, see: Jest docs: The 4 ways to create an ES6 class mock. One of the ways is described under: https://jestjs.io/docs/es6-class-mocks#mocking-non-default-class-exports
So you can use jest.mock() with your module path, then return an object with the key that is the same as the class export name.
const mockStartServer = jest.fn();
jest.mock("./Server/Server", () => {
return {
Server: jest.fn().mockImplementation(() => {
return { startServer: mockStartServer };
}),
};
});
Note that mockStartServer is defined and initialized with jest.fn() on the outer scope, and outside the returned object, and that's because Jest's mock behavior:
"Since calls to jest.mock() are hoisted to the top of the file, Jest
prevents access to out-of-scope variables. By default, you cannot
first define a variable and then use it in the factory. Jest will
disable this check for variables that start with the word mock.
However, it is still up to you to guarantee that they will be
initialized on time. Be aware of Temporal Dead Zone". src: jest documentation
Then the test would be:
import { Launcher } from "./luncher";
import { Server } from "./Server/Server";
const mockStartServer = jest.fn();
jest.mock("./Server/Server", () => {
return {
Server: jest.fn().mockImplementation(() => {
return { startServer: mockStartServer };
}),
};
});
it("should check for server start", () => {
const luncher = new Launcher();
luncher.launchApp();
expect(mockStartServer).toHaveBeenCalled();
});
You can also create a mock with spyOn, by overriding the implementation of the spied Server.prototype.startServer method.
const startServerMock = jest
.spyOn(Server.prototype, "startServer")
.mockImplementation(() => {});
it("should check for server start", () => {
const lun = new Launcher();
lun.launchApp();
expect(startServerMock).toHaveBeenCalled();
});

Jest - Mock a require thats inside a function

The function below is custom code we provide to a third party, hence the weird syntax.
const assignNameFunction = (exports.onExecutePostLogin = async (
event
) => {
const package = require("foo#2.23.0").Client;
package.doThing(event.bar)
});
I need to mock the package to unit test, one of the issues is the version. Due to the third party, I have to specify the version.
I tried the a few variants of:
import { thirdPartyFunction } from "./";
const mockFunc = jest.fn();
jest.mock("foo#2.23.0", ({
Client: {
doThing: mockFunc
}
}));
describe("Third Party Function", () => {
it("doThing is called", async () => {
const event = { bar: "foo" };
await thirdPartyFunction(event)
expect(mockFunc).toBeCalled();
});
});
or instead of mock using
import all from "foo";
jest.spyOn(all, "Client").mockReturnValue(...);
But jest comes back with Cannot find module 'foo#2.23.0'
How can I mock the require from within the function?

jasmine spy not finding property

I have a file that basically looks like this(shortened)
const octokit = new (require("#octokit/rest"))();
function buildRepo(name) {
fs.promises
.readFile("data/settings.json")
.then(data => JSON.parse(data))
.then(settings => settings.repositories.find(repo => repo.name === name))
.then(repo => {
let repoName = repo.url
.substring(repo.url.lastIndexOf("/") + 1)
.slice(0, -4);
let jobName = repo.name;
return octokit.repos
.get({
owner: "munhunger",
repo: repoName
})
.then(({ data }) => {
...
});
});
}
module.exports = { buildRepo };
And so I want to write a test for what it does with the data that it gets from the octokit.repos.get function. But since that function will go out to the internet and look at GitHub repositories, I want to mock it.
I have a few tests running with jasmine, and I read up slightly on it and it seems as if jasmine should be able to mock this for me.
However, the test that I have written seems to fail.
const builder = require("./index");
describe("spyOn", () => {
it("spies", () => {
spyOnProperty(builder, "octokit");
builder.buildRepo("blitzbauen");
});
});
With the error octokit property does not exist. What am I doing wrong here? would I need to add octokit to module.exports?(which seems rather insane)
Yes, you'd need to add Octokit to module.exports since you now only export buildRepo.
Anything from a module that is not exported can't be accessed directly by other modules, so if it should be accessible it should be exported.
Alternatively, you may be able to mock the entire Octokit module with Jasmine so any calls by any script are made to the mocked version, but I'm not sure how you'd go about doing that since my experience with Jasmine is limited

Jest testing context / spy on mocked variables created outside of functions (class level) Postmark

I'm trying to do some testing in Jest but getting stuck with a mock/spy. I've managed to get the test working but only by changing my implementation (which I feel dirty about).
Here's the test:
import * as postmark from 'postmark';
jest.mock('postmark');
const mockGetServers = jest.fn();
const AccountClient = jest.fn(() => {
return {
getServers: mockGetServers
};
});
postmark.AccountClient = AccountClient;
import accountApi from './account-api';
describe('account-api', () => {
describe('listServers', () => {
it('calls postmark listServers', async () => {
await accountApi.listServers();
expect(mockGetServers).toHaveBeenCalledTimes(1);
});
});
});
Here's the working implementation:
import * as postmark from 'postmark';
const accountToken = 'some-token-number';
const listServers = async () => {
try {
const accountClient = postmark.AccountClient(accountToken);
const servers = await accountClient.getServers();
return servers;
} catch (e) {
console.log('ERROR', e);
}
};
export default {
listServers
}
Here's the original implementation:
import * as postmark from 'postmark';
const accountToken = 'some-token-number';
const accountClient = postmark.AccountClient(accountToken);
const listServers = async () => {
try {
const servers = await accountClient.getServers();
return servers;
} catch (e) {
console.log('ERROR', e);
}
};
export default {
listServers
}
The only change is where in the code the accountClient is created (either inside or outside of the listServers function). The original implementation would complete and jest would report the mock hadn't been called.
I'm stumped as to why this doesn't work to start with and guessing it's something to do with context of the mock. Am I missing something about the way jest works under the hood? As the implementation of accountApi will have more functions all using the same client it makes sense to create one for all functions rather than per function. Creating it per function doesn't sit right with me.
What is different about the way I have created the accountClient that means the mock can be spied on in the test? Is there a way I can mock (and spy on) the object that is created at class level not at function level?
Thanks
Am I missing something about the way jest works under the hood?
Two things to note:
ES6 import calls are hoisted to the top of the current scope
babel-jest hoists calls to jest.mock to the top of their code block (above everything including any ES6 import calls in the block)
What is different about the way I have created the accountClient that means the mock can be spied on in the test?
In both cases this runs first:
jest.mock('postmark');
...which will auto-mock the postmark module.
Then this runs:
import accountApi from './account-api';
In the original implementation this line runs:
const accountClient = postmark.AccountClient(accountToken);
...which captures the result of calling postmark.AccountClient and saves it in accountClient. The auto-mock of postmark will have stubbed AccountClient with a mock function that returns undefined, so accountClient will be set to undefined.
In both cases the test code now starts running which sets up the mock for postmark.AccountClient.
Then during the test this line runs:
await accountApi.listServers();
In the original implementation that call ends up running this:
const servers = await accountClient.getServers();
...which drops to the catch since accountClient is undefined, the error is logged, and the test continues until it fails on this line:
expect(mockGetServers).toHaveBeenCalledTimes(1);
...since mockGetServers was never called.
On the other hand, in the working implementation this runs:
const accountClient = postmark.AccountClient(accountToken);
const servers = await accountClient.getServers();
...and since postmark is mocked by this point it uses the mock and the test passes.
Is there a way I can mock (and spy on) the object that is created at class level not at function level?
Yes.
Because the original implementation captures the result of calling postmark.AccountClient as soon as it is imported, you just have to make sure your mock is set up before you import the original implementation.
One of the easiest ways to do that is to set up your mock with a module factory during the call to jest.mock since it gets hoisted and runs first.
Here is an updated test that works with the original implementation:
import * as postmark from 'postmark';
jest.mock('postmark', () => { // use a module factory
const mockGetServers = jest.fn();
const AccountClient = jest.fn(() => {
return {
getServers: mockGetServers // NOTE: this returns the same mockGetServers every time
};
});
return {
AccountClient
}
});
import accountApi from './account-api';
describe('account-api', () => {
describe('listServers', () => {
it('calls postmark listServers', async () => {
await accountApi.listServers();
const mockGetServers = postmark.AccountClient().getServers; // get mockGetServers
expect(mockGetServers).toHaveBeenCalledTimes(1); // Success!
});
});
});
I think you might want to look at proxyquire.
import * as postmark from 'postmark';
import * as proxyquire from 'proxyquire';
jest.mock('postmark');
const mockGetServers = jest.fn();
const AccountClient = jest.fn(() => {
return {
getServers: mockGetServers
};
});
postmark.AccountClient = AccountClient;
import accountApi from proxyquire('./account-api', postmark);
describe('account-api', () => {
describe('listServers', () => {
it('calls postmark listServers', async () => {
await accountApi.listServers();
expect(mockGetServers).toHaveBeenCalledTimes(1);
});
});
});
Note that I have not tested this implementation; tweaking may be required.

How to mock const method in jest?

I unit test code in typescript, use jest. Please teach me how to mock getData to return the expected value. My code as below:
// File util.ts
export const getData = async () => {
// Todo something
return data;
}
// File execution.ts import { getData } from './util';
function execute()
{
// todo something
const data = await getData();
// todo something
}
The problem is that your function returns a promise. Depends on how you use it there are several ways to mock it.
The simplest way would be to mock it directly, but then it will always return the same value:
// note, the path is relative to your test file
jest.mock('./util', () => ({ getData: () => 'someValue' }));
If you want to test both the resolved and the rejected case you need to mock getData so it will return a spy where you later on can change the implementation use mockImplementation. You also need to use async/await to make the test work, have a look at the docs about asynchronous testing:
import { getData } from './util';
jest.mock('./util', () => ({ getData: ()=> jest.fn() }));
it('success case', async () => {
const result = Promise.resolve('someValue');
getData.mockImplementation(() => result);
// call your function to test
await result; // you need to use await to make jest aware of the promise
});
it('error case', async () => {
const result = Promise.reject(new Error('someError'));
getData.mockImplementation(() => result);
// call your function to test
await expect(result).rejects.toThrow('someError');
});
Try the following in your test file.
Import the function from the module.
import { getData } from './util';
Then mock the module with the function and its return value after all the import statements
jest.mock('./util', () => ({ getData: jest.fn() }))
getData.mockReturnValue("abc");
Then use it in your tests.
Because mocking expression functions can be a real pain to get right, I'm posting a full example below.
Scenario
Let's say we want to test some code that performs some REST call, but we don't want the actual REST call to be made:
// doWithApi.ts
export const doSomethingWithRest = () => {
post("some-url", 123);
}
Where the post is a function expression in a separate file:
// apiHelpers.ts
export const post = (url: string, num: number) => {
throw Error("I'm a REST call that should not run during unit tests!");
}
Setup
Since the post function is used directly (and not passed in as a parameter), we must create a mock file that Jest can use during tests as a replacement for the real post function:
// __mocks__/apiHelpers.ts
export const post = jest.fn();
Spy and Test
Now, finally inside the actual test, we may do the following:
// mockAndSpyInternals.test.ts
import {doSomethingWithRest} from "./doWithApi";
afterEach(jest.clearAllMocks); // Resets the spy between tests
jest.mock("./apiHelpers"); // Replaces runtime functions inside 'apiHelpers' with those found inside __mocks__. Path is relative to current file. Note that we reference the file we want to replace, not the mock we replace it with.
test("When doSomethingWithRest is called, a REST call is performed.", () => {
// If we want to spy on the post method to perform assertions we must add the following lines.
// If no spy is wanted, these lines can be omitted.
const apiHelpers = require("./apiHelpers");
const postSpy = jest.spyOn(apiHelpers, "post");
// Alter the spy if desired (e.g by mocking a resolved promise)
// postSpy.mockImplementation(() => Promise.resolve({..some object}))
doSomethingWithRest();
expect(postSpy).toBeCalledTimes(1)
expect(postSpy).toHaveBeenCalledWith("some-url", 123);
});
Examples are made using Jest 24.9.0 and Typescript 3.7.4

Categories