How to mock different hoist module value in jest - javascript

I'm trying to test geUrl() function in the file.
The first test case is passing but the second one will fail.
Because isFromApp is called only once on load of the file. I could not find a way to change the mock for it.
Any one know how can I mock different value for isFromApp for the second test case?
util.js
import { fromApp } from 'utils/domUtil';
export const isFromApp = fromApp('query', 'app');
export function getUrl() {
if (isFromApp) return 'is from app';
return 'is from web';
}
util.test.js
import { getUrl } from './util';
jest.mock('utils/domUtil', () => {
const actual = jest.requireActual('utils/domUtil');
return {
...actual,
fromApp: jest.fn().mockReturnValueOnce(true).mockReturnValueOnce(false),
};
});
describe('util', () => {
describe('getUrl', () => {
it('should return from app', () => {
expect(getUrl()).toEqual('is from app');
});
it('should return from web', () => {
expect(getUrl()).toEqual('is from web');
});
});
});

Related

How to mock DynamoDBDocumentClient constructor with Jest (AWS SDK V3)

I'm working with Jest to mock my AWS services, and more specifically DynamoDB and DynamoDBDocumentClient.
My code is currently similar to this :
import { DynamoDBClient } from "#aws-sdk/client-dynamodb"
import { DynamoDBDocumentClient } from "#aws-sdk/lib-dynamodb"
const ddbClient = new DynamoDBClient({})
const ddbDocumentClient = DynamoDBDocumentClient.from(ddbClient, config)
and the test spec looks like this:
jest.mock("#aws-sdk/client-dynamodb", () => ({
DynamoDBClient: jest.fn(() => ({
put: (params) => mockAwsResponse("DynamoDBClient", "GetCommand", params),
put: (params) => mockAwsResponse("DynamoDBClient", "PutCommand", params),
})),
}))
jest.mock("#aws-sdk/lib-dynamodb", () => ({
DynamoDBDocumentClient: jest.fn(() => ({
get: (params) => console.log("In DynamoDBClient get", params),
put: (params) => mockAwsResponse("DynamoDBDocumentClient", "PutCommand", params),
from: (params) => mockAwsResponse("DynamoDBDocumentClient", "from", params),
})),
}))
Unfortunately Jest returns me this error : TypeError: lib_dynamodb_1.DynamoDBDocumentClient.from is not a function and I believe it's because I'm incorrectly mocking DynamoDBDocumentClient, as its constructor is a static method.
Thanks in advance for your help!
EDIT: Just noticed that it used the AWS SDK v3 for JavaScript, if that helps.
In your example, you import your modules and run call your constructor right away (before entering any method in your script), this means that the object you are trying to mock needs to be available before entering any test methods.
Try to place your mocks before (and outside of) your tests something like this:
import { DynamoDBClient } from "#aws-sdk/client-dynamodb"
import { DynamoDBDocumentClient } from "#aws-sdk/lib-dynamodb"
/* whatever constants you need for your mocks */
jest.mock('#aws-sdk/client-dynamodb', () => {
return {
DynamoDBClient: jest.fn().mockImplementation(() => {
return {};
}),
};
});
jest.mock('#aws-sdk/lib-dynamodb', () => {
return {
DynamoDBDocumentClient: {
from: jest.fn().mockImplementation(() => {
return {
send: jest.fn().mockImplementation((command) => {
let res = 'something';
if(command.name == 'GetCommand'){
res = 'something else (a constant from earlier...)';
}
/* return whatever needs to be returned... */
return Promise.resolve(res);
}),
};
}),
},
/* Return your other docClient methods here too... */
GetCommand: jest.fn().mockImplementation(() => {
return { name: 'GetCommand' };
}),
};
});
// then you can implement your tests
describe('Endpoint something', () => {
it('Should pass or something...', async () => {
/* your test here... */
});
});
Now your functions should be available when they are needed.

expect(jest.fn()).toHaveBeenCalled() fails even though the function has been called

I'm trying to unit test a function that returns a promise. I'm having challenges verifying if
the mocked functions are called. Here's what I've done.,
// codetotest.js
const { SomeModule, isSomething, isSomethingElse } = require("some-module");
exports.somefunction = (param1, param2)=> {
const someModule = new SomeModule();
someModule.someMethod("aaa", isSomething);
someModule.someMethod("bbb", isSomethingElse);
return (someModule.someOtherMethod(param1)
.then(()=>{someModule.run(param2)}));
}
And this is the test file, the test says the mocked functions are not called, but I do see the console statement in the mock function is being displayed.
// codetotest.test.js
const { somefunction} = require("./codetotest.js");
const { SomeModule } = require("some-module");
jest.mock("some-module", () => {
return {
SomeModule: jest.fn().mockImplementation(() => {
return {
someMethod: jest.fn((param, fn) => { console.log("This prints!"); }),
someOtherMethod: jest.fn((param) => { return Promise.resolve(() => { }) }),
run: jest.fn((param) => { return Promise.resolve(() => { return []; }) })
}
})
};
});
afterEach(() => {
jest.resetAllMocks();
jest.restoreAllMocks();
});
describe("Test codetotest.js", () => {
it("somefunction() - success", async () => {
const someModule = new SomeModule();
let output = await somefunction("param1", "param2");
expect(SomeModule).toHaveBeenCalled();
expect(someModule.someMethod).toHaveBeenCalled(); // This fails
await expect(someModule.someOtherMethod.mock.results[0]).resolves;
expect(someModule.someOtherMethod).toHaveBeenCalled(); // This fails
await expect(someModule.run.mocks.results[0]).resolves;
expect(someModule.run).toHaveBeenCalled(); // This fails
});
});
Appreciate any help/pointers.
Thank you.
P.S: I'm still a beginner when it comes to nodeJs development and unit testing.
I spent quite some time on this and finally figured the instantiated mock class didn't return the mocked methods properly. This answer gave me a hint on where I was going wrong.
So accordingly, I had to change my test file as follows.,
// codetotest.test.js
const { somefunction} = require("./codetotest.js");
const { SomeModule } = require("some-module");
jest.mock("some-module", function() {
return {
SomeModule: jest.fn().mockImplementation(function() { // Arrow function cannot be used as constructor
// Because I was not using the 'this' operator, my constructor always returned empty
this.someMethod = jest.fn((param, fn) => { console.log("This prints!"); });
this.someOtherMethod = jest.fn((param) => { return Promise.resolve(() => { }) });
this.run = jest.fn((param) => { return Promise.resolve(() => { return []; }) });
return {
someMethod: this,someMethod,
someOtherMethod: this.someOtherMethod,
run: this.run
}
})
};
});
afterEach(() => {
jest.restoreAllMocks();
});
describe("Test codetotest.js", () => {
it("somefunction() - success", async () => {
await somefunction("param1", "param2");
expect(SomeModule).toHaveBeenCalled();
expect(SomeModule.mock.instances[0].someMethod).toHaveBeenCalled(); // This works
expect(SomeModule.mock.instances[0].someOtherMethod).toHaveBeenCalled(); // This works
expect(someModule.mock.instances[0].run).toHaveBeenCalled(); // This works
});
});

How do I mock this method chain in Jest?

zoomOut(callback) {
// Zooms out the current screen
this.view.current.zoomOut(300).done(() => {
(hasCallback(callback)) && callback();
});
}
I'm trying to test the function above but I keep getting the following error:
TypeError: this.view.current.zoomOut(...).done is not a function
How can I mock this method chain in Jest?
Thanks to BudgieInWA, I was able to solve this problem by returning done.
For those who are testing a React component with Enzyme, here's how you can do it:
it('should call callback', () => {
const wrapper = shallow(<Zoom {...minProps}/>);
const instance = wrapper.instance();
const callback = jest.fn();
instance.view = {
current: {
zoomOut: jest.fn(() => {
return {
done: jest.fn((callback) => {
callback();
})
};
})
}
};
expect(callback).toHaveBeenCalledTimes(0);
instance.zoomOut(callback);
expect(callback).toHaveBeenCalledTimes(1);
});
You could try this:
const mockZoomOut = jest.fn(() => ({ done(cb) { cb(); } }));
const mockThis = {
view: {
current: {
zoomOut: mockZoomOut,
},
},
};
test('it does', () => {
const cb = jest.fn();
zoomOut.apply(mockThis, [cb]);
expect(mockZoomOut).toHaveBeenCalledTimes(1);
expect(cb).toHaveBeenCalledTimes(1);
});
See Jest Mock Functions and fn.apply.
If you are testing the behaviour of the class as a whole, then you could set up the instance that you are testing to have this.view.current.zoomOut be mockZoomOut somehow.

Mocking up static methods in jest

I am having trouble mocking up a static method in jest. Immagine you have a class A with a static method:
export default class A {
f() {
return 'a.f()'
}
static staticF () {
return 'A.staticF()'
}
}
And a class B that imports A
import A from './a'
export default class B {
g() {
const a = new A()
return a.f()
}
gCallsStaticF() {
return A.staticF()
}
}
Now you want to mock up A. It is easy to mock up f():
import A from '../src/a'
import B from '../src/b'
jest.mock('../src/a', () => {
return jest.fn().mockImplementation(() => {
return { f: () => { return 'mockedA.f()'} }
})
})
describe('Wallet', () => {
it('should work', () => {
const b = new B()
const result = b.g()
console.log(result) // prints 'mockedA.f()'
})
})
However, I could not find any documentation on how to mock up A.staticF. Is this possible?
You can just assign the mock to the static method
import A from '../src/a'
import B from '../src/b'
jest.mock('../src/a')
describe('Wallet', () => {
it('should work', () => {
const mockStaticF = jest.fn().mockReturnValue('worked')
A.staticF = mockStaticF
const b = new B()
const result = b.gCallsStaticF()
expect(result).toEqual('worked')
})
})
Hope this will help you
// code to mock
export class AnalyticsUtil {
static trackEvent(name) {
console.log(name)
}
}
// mock
jest.mock('../src/AnalyticsUtil', () => ({
AnalyticsUtil: {
trackEvent: jest.fn()
}
}))
// code to mock
export default class Manager {
private static obj: Manager
static shared() {
if (Manager.obj == null) {
Manager.obj = new Manager()
}
return Manager.obj
}
nonStaticFunc() {
}
}
// mock
jest.mock('../src/Manager', () => ({
shared: jest.fn().mockReturnValue({
nonStaticFunc: jest.fn()
})
}))
// usage in code
someFunc() {
RNDefaultPreference.set('key', 'value')
}
// mock RNDefaultPreference
jest.mock('react-native-default-preference', () => ({
set: jest.fn()
}))
// code to mock
export namespace NavigationActions {
export function navigate(
options: NavigationNavigateActionPayload
): NavigationNavigateAction;
}
// mock
jest.mock('react-navigation', () => ({
NavigationActions: {
navigate: jest.fn()
}
}))
I managed to mock it in a separate file in the __mocks__ folder using prototyping. So you would do:
function A() {}
A.prototype.f = function() {
return 'a.f()';
};
A.staticF = function() {
return 'A.staticF()';
};
export default A;
Here's an example with an ES6 import.
import { MyClass } from '../utils/my-class';
const myMethodSpy = jest.spyOn(MyClass, 'foo');
describe('Example', () => {
it('should work', () => {
MyClass.foo();
expect(myMethodSpy).toHaveBeenCalled();
});
});
We need to create a mock and give visibility for the mocked method to the test suite. Below full solution with comments.
let mockF; // here we make variable in the scope we have tests
jest.mock('path/to/StaticClass', () => {
mockF = jest.fn(() => Promise.resolve()); // here we assign it
return {staticMethodWeWantToMock: mockF}; // here we use it in our mocked class
});
// test
describe('Test description', () => {
it('here our class will work', () => {
ourTestedFunctionWhichUsesThisMethod();
expect(mockF).toHaveBeenCalled(); // here we should be ok
})
})
Using Object.assign on the mock constructor allows simultaneous mocking of the class and its static methods. Doing this allows you to achieve the same structure you get when creating a class with static members.
import A from '../src/a'
import B from '../src/b'
jest.mock('../src/a', () =>
Object.assign(
jest.fn(
// constructor
() => ({
// mock instance here
f: jest.fn()
})),
{
// mock static here
staticF: jest.fn(),
}
)
)
Jest Spies
I went with the route of using jest.spyOn.
Example
encryption.ts
export class Encryption {
static encrypt(str: string): string {
// ...
}
static decrypt(str: string): string {
// ...
}
}
property-encryption.spec.ts
import { Encryption } from './encryption'
import { PropertyEncryption } from './property-encryption'
describe('PropertyEncryption', () => {
beforeAll(() => {
jest
.spyOn(Encryption, 'encrypt')
.mockImplementation(() => 'SECRET')
jest
.spyOn(Encryption, 'decrypt')
.mockImplementation(() => 'No longer a secret')
})
it("encrypts object values and retains the keys", () => {
const encrypted = PropertyEncryption.encrypt({ hello: 'world' });
expect(encrypted).toEqual({ hello: 'SECRET' });
});
it("decrypts object values", () => {
const decrypted = PropertyEncryption.decrypt({ hello: "SECRET" });
expect(decrypted).toEqual({ hello: 'No longer a secret' });
});
})

fetch-mock with jasmine not triggering then

I have this constant:
export const clientData = fetch(`${process.env.SERVER_HOST}clientData.json`)
.then(response => response.json());
Which works properly, and Now I'm working on the test of this, with Jasmine and fetch-mock
This is my test:
import { clientData } from '../../../src/js/services/client-data.fetch';
import fetchMock from 'fetch-mock';
describe('test', () => {
const exampleResponse = {
clientData: 'test'
};
beforeAll(() => {
fetchMock.mock('*', exampleResponse);
});
it('ooo', () => {
console.log('here', clientData);
var a = clientData;
a.then(b=> console.log(b))
});
});
The console.log of clientData returns a Promise (Which is fine), but the then is never triggered.
Not seeing why, what is wrong with my code?
This happens because the test execution is synchronous in nature and it doesn't wait for the assertion to happen, so you have to pass a done callback and call it from your test inside the then callback
Like this:
import { clientData } from '../../../src/js/services/client-data.fetch';
import fetchMock from 'fetch-mock';
describe('test', () => {
const exampleResponse = {
clientData: 'test'
};
beforeAll(() => {
fetchMock.mock('*', exampleResponse);
});
it('ooo', (done) => {
console.log('here', clientData);
var a = clientData;
a.then(b=> {
console.log(b);
done();
})
});
});

Categories

Resources