//test.js
const map = {};
document.addEventListener = jest.fn((event, cb) => {
map[event] = cb;
});
const wrapper = mount(<AccordianDropdown />);
it('Should not close dropdown on document click inside', () => {
map.click({
target: <div role="button"/>,
});
});
This is my index.js file:-
// index.js
componentDidMount() {
document.addEventListener('click', this.closeOnDocumentClick);
}
componentWillUnmount() {
document.removeEventListener('click', this.closeOnDocumentClick);
}
closeOnDocumentClick = (e) => {
// If event happened in the dropdown, ignore it
if (!(this.ref && isFunction(this.ref.contains) && this.ref.contains(e.target)) && this.state.open) {
this.setState({ open: false });
}
};
On running test getting this error TypeError: Failed to execute 'contains' on 'Node': parameter 1 is not of type 'Node'.. Please help me understand how to test this piece of code.
Ok Found a solution for this,
In the wrapper object call the getDOMNode() method to get the DOM node(s) element(s).
If you pass this element as the event.target value. You will not get an exception.
If not you could create an element using JSDOM
const dom = new JSDOM();
const newDiv = dom.window.document.createElement("div");
and then
test('...', () => {
const mockedEvent = { target: wrapper.getDOMNode()[0] };
wrapper
.find('ClickAwayListener')
.props()
.onClickAway(mockedEvent);
});
or If you just want to avoid the exception, TypeError: Failed to execute 'contains' on 'Node': parameter 1 is not of type 'Node'. pass null as the target to the simulated event or event callback handler.
test('...', () => {
const mockedEvent = { target: null };
wrapper
.find('ClickAwayListener')
.props()
.onClickAway(mockedEvent);
});
Hope this code will help you.
Related
After much trial/error, searching here on SO, & flexing my Google Fu, throwing in the towel & asking for help.
TL/DR -
Trying to correctly mock node module, change internal method return types, and spy on ctor's & method calls within the node module.
My specific scenario is to test the Microsoft Azure Storage blob SDK #azure/storage-blob, but the questions aren't specific to this package. It's just a good example as 4 LOC's capture achieve a task (upload a file to a storage container) as 2-3 of those LOC's cover 4 scenarios. Here's my code that I want to test, with comments on WHAT exactly I want to test:
export async function saveImage(account: string, container: string, imageBuffer: Buffer, imageName: string): Promise<void> {
try {
// init storage client
// (1) want to spy on args passed into ctor
const blobServiceClient: BlobServiceClient = new BlobServiceClient(`https://${account}.blob.core.windows.net`, new DefaultAzureCredential());
// init container
// (2) same as #1
const containerClient: ContainerClient = await blobServiceClient.getContainerClient(container);
// init block blob client
// (3) same as #1 & #2
const blockBlobClient: BlockBlobClient = containerClient.getBlockBlobClient(imageName);
// save file
// (4) same as #1,2, & 3
// (5) manipulate returned value
// (6) throw cause method to internally throw error
await blockBlobClient.upload(imageBuffer, imageBuffer.length, { blobHTTPHeaders: { blobContentType: 'image/png' } });
return Promise.resolve();
} catch (err: Error) {
return Promise.reject(err);
}
}
I've setup a manual mock for the module in the ./__mocks/#azure/storage-blob.ts as follows:
const MockStorageBlob = jest.createMockFromModule('#azure/storage-blob');
/**
* Utility method throw exception in the `BlockBlobClient.upload()` method.
*/
(MockStorageBlob as any).__setBlockBlobUpload_toFail = () => {
(MockStorageBlob as any).BlobServiceClient = jest.fn().mockImplementation(() => {
return {
getContainerClient: jest.fn().mockReturnValue({
getBlockBlobClient: jest.fn().mockReturnValue({
upload: jest.fn().mockImplementation(() => {
throw new Error('synthetic error');
})
})
})
}
});
}
module.exports = MockStorageBlob;
In my test, I can successfully test for #6 above like this:
import {
BlockBlobClient,
BlockBlobUploadResponse
} from '#azure/storage-blob';
import { saveImageToCDN as functionUnderTest } from './saveImageToCDN';
// mock Azure Storage blob NPM package
jest.mock('#azure/storage-blob');
describe('check expected with failure', () => {
beforeEach((done) => {
// reset number of times things have been called
jest.clearAllMocks();
done();
});
test(`it calls 'trackException()' when upload throws exception`, async (done) => {
expect.assertions(1);
// setup test
require('#azure/storage-blob').__setBlockBlobUpload_toFail();
// run SUT
const imageBuffer = Buffer.from('test string');
functionUnderTest(imageBuffer, 'imageName.png')
.then(() => {
expect(new Error('should not reach this')).toBeUndefined();
})
.catch((err: Error) => {
expect(err).toBeDefined();
})
.finally(() => {
done();
});
});
});
... but I can't figure out the correct syntax to spy on the upload() method (#4), or any of the other things I'm trying to test for (#1-5). If it matters, using Jest v26 on Node v14.
Could the __setBlockBlobUpload_toFail function return references to the mock functions ?
That would give something like this :
const MockStorageBlob = jest.createMockFromModule('#azure/storage-blob');
/**
* Utility method throw exception in the `BlockBlobClient.upload()` method.
*/
(MockStorageBlob as any).__setBlockBlobUpload_toFail = () => {
const upload = jest.fn().mockImplementation(() => {
throw new Error('synthetic error');
});
const getBlockBlobClient = jest.fn().mockReturnValue({ upload });
const getContainerClient = jest.fn().mockReturnValue({ getBlockBlobClient });
const BlobServiceClient = jest.fn().mockImplementation(() => {
return {
getContainerClient
}
});
(MockStorageBlob as any).BlobServiceClient = BlobServiceClient;
return {
upload,
getBlockBlobClient,
getContainerClient,
BlobServiceClient
};
}
module.exports = MockStorageBlob;
And in your test you would retrieve them like :
// setup test
const mockFns = require('#azure/storage-blob').__setBlockBlobUpload_toFail();
// run SUT
const imageBuffer = Buffer.from('test string');
functionUnderTest(imageBuffer, 'imageName.png')
.then(() => {
expect(new Error('should not reach this')).toBeUndefined();
})
.catch((err: Error) => {
expect(mockFns.getBlockBlobClient.mock.calls[0][0]).toBe('imageName.png')
expect(err).toBeDefined();
})
.finally(() => {
done();
});
I'm trying to mock function that has a stream return type and callback as arg. I get timeout because the call back function never get called.
This is the function I'm trying to test:
public setServerUrls(): Observable<void> {
const obs: Observable<void> = new Observable((observer: Observer<void>) => {
const stream: ClientWritableStream<Util.Server> = this.fileTransferClient.setURLs((error: ServiceError, response) => {
if (error) {
observer.error(`TransferManager Electron: unable to set server urls error: ${error.message}.`);
}
observer.next();
observer.complete();
logger.info(`TransferManager Electron: setting URLs completed.`);
});
const transferRequest = new Util.Server();
transferRequest
.setIp('localhost')
.setTransferport('9092')
.setUuid('4020a522-81fe-4996-b637-0620ae656d29');
stream.write(transferRequest);
stream.end();
});
return obs;
}
My jasmine setup:
const settingStream = {
write: () => { },
end: () => { }
};
const callBack = () => {
return;
};
const f = function (callback: Function): any {
return settingStream;
};
mockFileTransferClient = jasmine.createSpyObj('FileTransferClient', {
subscribe: () => mockFileTransferStream,
uploadFile: () => duplexStream,
setURLs: f(callBack)
});
mockFileTransferClientWrapper.createNewFileTransferClient.and.returnValue(mockFileTransferClient as any);
// Question here... does not work!
mockFileTransferClient.setURLs.and.returnValue(settingStream);
test:
it('should set urls', done => {
transferManager.setServerUrls()
.subscribe(
x => {
expect(x).toBeDefined();
done();
}
);
});
This is Elctron + grpc functionality test.
Test timeout because it never gets into the callback and observable never completes.
I'm not sure how to mock setURLs with return value AND callback.
You have to first mock your setUrls to return a resolved observable.
I think what you need here is to use fakeAsync() functionality as you are dealing with async test. Using Tick with fakeAsync ensures that all pending asynchronous activities will finish. In your case it will wait for setTimeout.
Note that you should also inject the setServerUrls using TestBed.inject...
So I think your tests should look like this
it("should .....", fakeAsync(() => {
// Arrange - mock functions
// Act - Make a call to the function
// Use tick() after making call to the function which will flush your setTimeouts
// Assert
}));
For more information, see the fakeAsync documentation
I'm using NodeJS and a MongoDB. I have this simple function for returning a generic property of a document ...
import mongoose, { Document, Schema } from "mongoose";
export interface IMyObject extends Document {
...
}
...
export async function getProperty(
req: CustomRequest<MyDto>,
res: Response,
next: NextFunction
): Promise<void> {
const {
params: { propertyName, code },
} = req;
try {
const my_obj = await MyObject.findOne({ code });
const propertyValue = my_obj ? my_obj.get(propertyName) : null;
if (propertyValue) {
res.status(200).json(propertyValue);
...
I'm struggling to figure out how to test this function. In particular, how do I mock an instance of my object that's compatible with the "get" method? I tried this
it("Should return the proper result", async () => {
const myObject = {
name: "jon",
};
MyObject.findOne = jest.fn().mockResolvedValue(myObject.name);
const resp = await superTestApp.get(
"/getProperty/name/7777"
);
expect(resp.status).toBe(StatusCodes.OK);
expect(resp.body).toEqual("happy");
but this fails with
TypeError: my_object.get is not a function
You would need to spy your object and its methods. Something like:
import MyObject from '..';
const mockedData = {
get: (v) => v
};
let objectSpy;
// spy the method and set the mocked data before all tests execution
beforeAll(() => {
objectSpy = jest.spyOn(MyObject, 'findOne');
objectSpy.mockReturnValue(mockedData);
});
// clear the mock the method after all tests execution
afterAll(() => {
objectSpy.mockClear();
});
// call your method, should be returning same content as `mockedData` const
test('init', () => {
const response = MyObject.findOne();
expect(response.get('whatever')).toEqual(mockedData.get('whatever'));
});
I am attempting to execute the following Jest test to test an AWS Lambda locally:
const sinon = require('sinon');
const AWS = require('aws-sdk');
const { handler } = require('../queue_manager.js');
let result = {
// some result
};
let sinonSandbox;
beforeEach((done) => {
sinonSandbox = sinon.createSandbox();
done();
})
afterEach((done) => {
sinonSandbox.restore()
done();
})
it('queue-manager', async () => {
sinonSandbox.stub(AWS.DynamoDB.DocumentClient.prototype, 'get').returns({
promise: function () {
return Promise.resolve(result);
}
});
const lambdaResponse = { code: 200, data: 'some mocked data' };
var callback = function() { };
var context = {}
const event = {
somedata: "data"
};
const actualValue = await handler(event, context, callback);
expect(actualValue).toEqual(result);
});
I am attempting to test processing after a DynamoDB call, however, the test fails with a: Timeout - Async callback was not invoked within the 5000ms timeout specified by jest.setTimeout.Timeout
I tried giving it more time but the same result so it is not that it cannot return successfully in 5 seconds, it is not returning all.
Anyone familiar with Sinon that could possibly point out my issue?
Post edited, fist edition pushed at the bottom of the answer a
Second thought
here is an example on how to test an asynchronous function with Jest
test('the data is peanut butter', done => {
function callback(data) {
try {
expect(data).toBe('peanut butter');
done();
} catch (error) {
done(error);
}
}
fetchData(callback);
});
then I think the missing part in your code is the done callback on the unit test function. could you try this:
it('queue-manager', async (done) => {
sinonSandbox.stub(AWS.DynamoDB.DocumentClient.prototype, 'get').returns({
promise: function () {
return Promise.resolve(result);
}
});
const lambdaResponse = { code: 200, data: 'some mocked data' };
var callback = function() { };
var context = {}
const event = {
somedata: "data"
};
const actualValue = await handler(event, context, callback);
expect(actualValue).toEqual(result);
done();
});
Another option could be to specify the number of assertions (instead of the callback done) :
// before the await handler(...)
expect.assertions(1);
Hope this help.
First lead was the dynamo db ressource hanging:
Quite often the lambda is not returning the result and then runs into timeout because the lambda function is waiting for resources ie: dynamoDB connection for instance.
You can configure the runtime to send the response immediately by setting context.callbackWaitsForEmptyEventLoop to false.
First line of the handler must be:
context.callbackWaitsForEmptyEventLoop = false
Given an component that make a login ...
onSubmit(e) {
e.preventDefault();
const { userStore, data } = this.props;
this.setState({isLogin: true});
const promise = userStore.login(data);
promise.then(() => {
this.setState({isLogin: false});
})
}
How can I create a test that validate if isLogin state is false after the promise is resolved?
So far, I have ..
it('resolved submit should set isLogin to false', () => {
mockApi.onPost('/authenticate').reply(200);
const userStore = new UserStore();
let fromPromiseLogin;
userStore.login = jest.fn()
.mockImplementationOnce((data) => {
fromPromiseLogin = userStore.login(data);
return fromPromiseLogin;
});
const wrapper = mount(
<FormLogin userStore={userStore} />,
);
wrapper.find('form').simulate('submit');
// HOW CAN I WAIT/OR CALLBACK FOR THE PROMISE ? I TRIED >
whenWithTimeout(
() => fromPromiseLogin && fromPromiseLogin.state !== PENDING,
() => expect(wrapper.state().isLogin).toBeFalsy(),
);
});
But a exeption
[mobx] Encountered an uncaught exception that was thrown by a reaction or observer component, in: 'Reaction[When#21] TypeError: Cannot read property 'state' of undefined
EDIT:
This answer is no longer relevant after the question has been edited.
Have you tried to test the result of the promise with .resolves function as described here?
expect(myPromise).resolves.toEqual('SomeValue');
Or using async/await:
const result = await myPromise;
expect(result).toEqual('SomeValue');