Jest - test try-catch block - javascript

I'm trying to test a try catch block using Jest. E.g.
// filename: foo.js
const foo = () => {
let js = 'somejs'
try {
eval(js)
} catch (e) {
document.location = 'redirect/to/page'
return false
}
return true
}
export default foo
Do you mock eval (not my choice to use eval. It's my bosses code) and then get it to throw? If so, how would you do that? E.g.
eval = jest.fn() // doesn't work
So the test code might be:
// filename: foo.spec.js
import foo from './foo'
eval = jest.fn()
describe('foo', () => {
describe('when eval succeeds', () => {
it('performs an eval', {
const result = foo()
expect(result).toEqual(true)
})
describe('when eval fails', () => {
beforeEach(() => {
eval.mockImplementation(() => {throw 'error'})
})
it('catches the error and redirects', () => {
const result = foo()
expect(result).toEqual(false)
})
})
})
What would the mocking and test code look like?

Related

How to mock different hoist module value in jest

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');
});
});
});

Jest mock a function which instantiates a class and its constructor has an error callback, callback not invoked in error case

I need to satisfy SonarQube for the error case in the code below.
When I run the test code though, it will not fail.
index.js
const mod = require('mod')
function get_modclass(data) {
let modclass = new ModClass(data, function(err) {
if (err) {
console.log('ERROR: ' + err);
throw err
}
})
return modclass
}
index.spec.js
describe('Test get_modclass', () => {
jest.resetModules()
jest.clearAllMocks()
test('Should fail get_modclass', () => {
const idx = require('./index.js');
const mod = require('mod')
jest.mock('mod', () => ({
ModClass: jest.fn(() => ({
constructor: (_data, cb) => cb('err'),
}))
}))
try {
let mc = idx.get_modclass("abc")
} catch(e) {
expect(e.message).toEqual('some message')
}
})
})
Rather than cb('err') I have tried sending cb(new Error('err message')) to the constructor, but it will still not fail. When I debug, it goes into the idx.get_modclass function, but from there it goes direct to return modclass instead of to the if of the callback.
Help appreciated.
Thanks!

how to unit test if handler function is called when using middy

im using an http request function as the handler function in middy and then use the ssm middleware to fetch some ssm parameters before initiating the http request.
like this:
const makeThirdPartyServiceRequest = middy(async ({ params }) => {
logger.info(`SENDING Request to ${endpoint} API`)
const url = `https://someurltoathirdpartyservice`
const options = {
method: 'POST',
body: params
}
return helpers.makeRequest(url, options)
})
makeThirdPartyServiceRequest.use(ssm(......))
However in my jest unit test Im trying to mock makeThirdPartyServiceRequest and explicitly say it should resolve to a value:
jest.mock('../src/thirdPartyService', () => ({
__esModule: true,
default: {
...(jest.requireActual('../src/thirdPartyService') as { default: {} }).default,
makeThirdPartyServiceRequest: jest.fn()
}
}))
export {}
import thirdPartyService from '../src/thirdPartyService'
And then in the test i say:
describe('makeThirdPartyServiceRequest()', () => {
it('should makeThirdPartyServiceRequest', async () => {
// Given
// })
const mockedThirdPartyServiceRequest = mocked(thirdPartyService.makeThirdPartyServiceRequest).mockResolvedValue({})
// When
const result = await thirdPartyService.makeThirdPartyServiceRequest(something)
// Then
expect(mockedThirdPartyServiceRequest).toHaveBeenCalledTimes(1)
expect(mockedThirdPartyServiceRequest.mock.calls[0][0].params.toString()).toBe(expectedParams)
})
})
However for some reason the middy middleware is still being invoked, which i clearly dont want and i have tried to mock away... what am i doing wrong?
You need to mock middy instead, to make it becomes a useless function. That function recipe a function as a parameter and return that parameter.
import thirdPartyService from '../src/thirdPartyService'
jest.mock('#middy/core', () => {
return (handler) => {
return {
use: jest.fn().mockReturnValue(handler), // ...use(ssm()) will return handler function
}
}
})
describe('thirdPartyService()', () => {
beforeEach(() => {
jest.spyOn(helpers, 'makeRequest') // spy on helpers unit
})
describe('makeThirdPartyServiceRequest', () => {
it('should make a request with correct parameters', async () => {
// Given
const url = `https://someurltoathirdpartyservice`
const params = 'any params'
const apiResponse = 'any response'
mocked(helpers.makeRequest).mockResolvedValue(apiResponse)
// When
const actual = await thirdPartyService.makeThirdPartyServiceRequest(params)
// Then
expect(actual).toBe(apiResponse)
expect(helpers.makeRequest).toHaveBeenCalledWith(
url,
{
method: 'POST',
body: params
}
)
})
})
})
hoangdv answer is also valid, but i will answer as well how i continued.
if you completely want to mock middy you mock like following:
jest.mock('#middy/core', () => {
return (handler) => {
return {
use: jest.fn().mockImplementation(() => {
// ...use(ssm()) will return handler function
return {
before: jest.fn().mockReturnValue(handler)
}
})
}
}
})
However if you dont want to completely mock middy, you can instead mock the async getInternal function from middy/util called in before like this:
jest.doMock('#middy/util', () => ({
...(jest.requireActual('#middy/util') as {}),
getInternal: jest.fn()
}))
import { getInternal } from '#middy/util'
and then in the test
describe('thirdPartyService()', () => {
beforeEach(() => {
jest.spyOn(helpers, 'makeRequest') // spy on helpers unit
})
describe('makeThirdPartyServiceRequest', () => {
it('should make a request with correct parameters', async () => {
// Given
const url = `https://someurltoathirdpartyservice`
const params = 'any params'
const apiResponse = 'any response'
mocked(getInternal).mockResolvedValue({
twilioSecrets: { accountSid: 'someSID', serviceId:
'someServiceID', token: 'someToken' }
})
mocked(helpers.makeRequest).mockResolvedValue(apiResponse)
// When
const actual = await thirdPartyService.makeThirdPartyServiceRequest(params)
// Then
expect(actual).toBe(apiResponse)
expect(helpers.makeRequest).toHaveBeenCalledWith(
url,
{
method: 'POST',
body: params
}
)
})
})
})
this will mock the async part of middy.

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