React Testing Library jest.fn() toHaveBeenCalled() not working - javascript

Component:
const MyComp = ({ handler }) => {
return <button onClick={handler}>Test</button>
};
Test:
it('Calls the handler', async () => {
const handler = jest.fn();
render(<MyComp handler={handler} />);
const button = screen.getByRole('button', { name: /Test/i });
await fireEvent(button, new MouseEvent('click'));
expect(handler).toHaveBeenCalled();
});
Expected number of calls: >= 1
Received number of calls: 0

Three options:
Make the event bubble, as shown in the example (it doesn't return a promise, no await needed):
fireEvent(button, new MouseEvent("click", { bubbles: true }));
Use the convenience method, which adds default event properties including bubbling (likewise):
fireEvent.click(button);
Use userEvent (does return a promise, as of v14):
await userEvent.click(button);

Try using userEvent.click, like this
it('Calls the handler', async () => {
const handler = jest.fn();
render(<MyComp handler={handler} />);
const button = screen.getByRole('button', { name: /Test/i });
await userEvent.click(button);
expect(handler).toHaveBeenCalled();
});

Related

How to mock SQL Server connection pool using Jest?

I am trying to write a jest unit test for a function that utilizes mssql.
import * as sql from "mssql";
let pool: sql.ConnectionPool = null;
export async function handler() {
if (pool === null) {
try {
pool = new sql.ConnectionPool("");
await pool
.request()
.input("SomeInput", sql.NVarChar(255), "input")
.execute("SomeStoredProcedure");
} catch (err) {
console.error(err);
}
}
}
What would be the simplest way to mock the sql methods and assert they have been called?
import { handler } from "../src/main";
describe("test handler", () => {
it("should succeed", async () => {
const requestFn = jest.fn();
const executeFn = jest.fn();
const inputFn = jest.fn();
// Mock mssql connection pool with above functions
// *????????*
await handler();
// Expect the functions have been called
expect(requestFn).toHaveBeenCalled();
expect(executeFn).toHaveBeenCalled();
expect(inputFn).toHaveBeenCalled();
});
});
Sandbox
You can mock the mssql package by using an jest ES6 Class Mocks. You can achive that by using:
const mockExecute = jest.fn();
const mockInput = jest.fn(() => ({ execute: mockExecute }));
const mockRequest = jest.fn(() => ({ input: mockInput }));
jest.mock('mssql', () => ({
ConnectionPool: jest.fn(() => ({
request: mockRequest
})),
NVarChar: jest.fn()
}));
Have a look at the Stackblitz project and run jest in the terminal. You should see that the tests are passing.
You need to mock the return value of each function in the chain. You can do this using jest.fn().mockImplementation(implementation)
Expanding you example to use this give us the following
import { handler } from "../src/main";
let pool;
describe("test handler", () => {
it("should succeed", async () => {
const requestFn = jest.fn();
const executeFn = jest.fn();
const inputFn = jest.fn();
pool = {
request: requestFn,
execute: executeFn,
inputFn: inputFn,
};
requestFn.mockImplementation(() => pool);
executeFn.mockImplementation(() => pool);
inputFn.mockImplementation(() => pool);
await handler();
// Expect the functions have been called
expect(requestFn).toHaveBeenCalled();
expect(executeFn).toHaveBeenCalled();
expect(inputFn).toHaveBeenCalled();
});
});

How to test a method dispatching custom event

I have a static method that creates a custom event and dispatches it:
class MyComponent extends {
static refreshComponent() {
const event = new Event('refreshComponent');
document.dispatchEvent(event);
}
render() {
MyComponent.refreshComponent();
}
}
I am trying test as below:
describe('refreshComponent', () => {
it('should dispatch an event', () => {
const document = { dispatchEvent: jest.fn() };
wrapper.refreshGoalsComponent();
expect(document.dispatchEvent).toHaveBeenCalledWith('refreshComponent');
});
});
But the dispatchEvent is not called here, as there is no mock for 'new Event()'. Is there a way to mock it? Please help
It's a little clunky looking, but something like this should work if you want to verify an event with the expected type was dispatched:
describe('refreshComponent', () => {
it('should dispatch an event', () => {
const dispatchEventSpy = jest.spyOn(document, 'dispatchEvent');
// Trigger component render here...
expect(dispatchEventSpy).toHaveBeenCalledWith(expect.any(Event));
expect(dispatchEventSpy.mock.calls[0][0].type).toBe('refreshComponent');
});
});
You can test dispatch event like this:
describe('refreshComponent', () => {
it('should dispatch an event', () => {
jest.spyOn(global, 'Event').mockImplementation((type: string, eventInit?: any) => ({ type, eventInit }));
const mockDispatchEvent = jest.spyOn(document, 'dispatchEvent').mockImplementation(() => true);
// render your component
expect(mockDispatchEvent).toHaveBeenCalledWith({
type: 'refreshComponent',
});
});
});
This way can expect event type and event init value. If you don't want to expect detail event, we does not need to mock Event and expect like:
expect(mockDispatchEvent).toHaveBeenCalledWith(expect.any(Event));
You can mock globals with Jest:
describe('your test', () => {
let EventBak
let documentBak
beforeAll(() => {
EventBak = global.Event
documentBak = global.document
global.Event = jest.fn()
global.document = {
...global.document,
dispatchEvent: jest.fn()
}
})
afterAll(() => {
global.Event = EventBak
global.document = documentBak
})
it('...', () => {
...
expect(global.document.dispatchEvent).toHaveBeenCalled()
})
})

Failed toHaveBeenCalled() jest test in Vue component

Not working function call with 'click' trigger in test.
I called function - it is working
I triggered click but test was failed.
describe("Message.test.js", () => {
let wrapper;
const createWrapper = propsData => mount(Message, { propsData
describe("Events", () => {
beforeEach(() => {
wrapper = createWrapper({ message: "Cat" });
});
//Working
it("calls handleClick", () => {
const spy = jest.spyOn(wrapper.vm, 'handleClick');
wrapper.vm.handleClick();
expect(spy).toHaveBeenCalled();
});
//NOT WORKING. WHY?
it("calls handleClick when click on message", () => {
wrapper.vm.handleClick = jest.fn();
//It is Ok
expect(wrapper.contains('.message')).toBe(true);
// #click="handleClick" on element
wrapper.find('.message').trigger('click');
expect(wrapper.vm.handleClick).toHaveBeenCalledTimes(1);
})
});
I added console.log to the function. During the test, I see what function was called.
it('calls handleClick when click on message', () => {
const handleClick = jest.fn()
wrapper.setMethods({ handleClick })
const el = wrapper.find('.message').trigger('click')
expect(handleClick).toBeCalled()
})
// stub
it('triggers a message-clicked event when a handleClick method is called', () => {
const stub = jest.fn()
wrapper.vm.$on('message-clicked', stub)
wrapper.vm.handleClick()
expect(stub).toBeCalledWith('Cat')
})

spying a function called in a rejected promise

I would like to spy a function to test if this function is called in a catch block when a promise is rejected. My code is a react component like this
export class ResetPassword extends Component {
handleSubmit = e => {
e.preventDefault();
this.props
.resetPassword()
.then(() => {
this.props.history.push(LOGIN_PATH);
})
.catch(({ error }) => {
this.props.displayErrorAlert('impossible to change password. You should ask for a new reset password link',6000);
});
};
}
Here I want to test if the function displayErrorAlert has been called. I made this test
it('validate form', () => {
const resetPassword = () => {
return Promise.reject({
error: {
response: {
data: {
errors: [
{
title: 'foo',
},
],
},
},
},
});
};
const displaySpy = sinon.spy();
const wrapper = mount(
<ResetPassword
history={{}}
resetPassword={resetPassword}
displayErrorAlert={displaySpy}
/>
);
wrapper.instance().handleSubmit({
preventDefault: () => {},
});
expect(displaySpy.calledOnce).toEqual(true);
});
The spy is called but asynchronously of course so my test always fails. I would like to find a way to test if the function has been called only once the catch block has been called and I have no idea how to do that.
Sinon provides you everything you need when handling promises, you can resolve and reject a stubbed promise using sinon.stub().
const resetPassword = sinon.stub();
const displayErrorAlert = sinon.spy();
const preventDefault = sinon.spy();
const props = {
resetPassword,
displayErrorAlert,
history: []
};
describe('Given a component', () => {
let component;
describe('when rendered', () => {
beforeAll(() => {
component = shallow(<ResetPassword {...props} />);
});
describe('and the form is submitted and there is an error reseting the password', () => {
beforeAll(() => {
resetPassword.rejects(new Error('Oops!'));
component.find('button').simulate('click', { preventDefault });
});
it('should invoke the displayErrorAlert function', () => {
expect(displayErrorAlert.calledOnce).toBeTruthy();
});
});
});
});
I found an other solution, I return the promise in the handleSubmit function and use it in my tests.
export class ResetPassword extends Component {
handleSubmit = e => {
e.preventDefault();
return this.props
.resetPassword()
.then(() => {
this.props.history.push(LOGIN_PATH);
})
.catch(({ error }) => {
this.props.displayErrorAlert('impossible to change password. You should ask for a new reset password link',6000);
});
};
}
and my test
it('validate form', () => {
const resetPassword = () => {
return Promise.reject({
error: {
response: {
data: {
errors: [
{
title: 'foo',
},
],
},
},
},
});
};
const displaySpy = sinon.spy();
const wrapper = mount(
<ResetPassword
history={{}}
resetPassword={resetPassword}
displayErrorAlert={displaySpy}
/>
);
expect.assertions(1);
const promise = wrapper.instance().handleSubmit({
preventDefault: () => {},
});
return promise.then(() => {
expect(displaySpy.calledOnce).toEqual(true);
});
});

how to assert an async action dispatched inside an async action in redux-thunk

I'm trying to assert an async action is dispatched by an async action like so :
// synchronous actions
export const getObjects = () => ({ type: 'GET_OBJECTS' });
export const addObject = object => ({ type: 'ADD_OBJECT', object });
// an async action
export const getObjectsAsync = () =>
dispatch =>
axios.get(URL).then((data) => {
dispatch(getObjects());
});
// another async action that dispatches the previous async action
export const postObjectAsync = newObject =>
dispatch =>
axios.post(URL, newObject)
.then(() => { dispatch(addObject(newObject)); })
.then(() => { dispatch(getObjectAsync()); });
// the test
describe('postObjectAsync()', () => {
it('should return ADD_OBJECT and GET_OBJECT actions', () => {
const object = mockedObject;
const store = mockedStore;
const expectedActions = [
{ type: 'ADD_OBJECT', object },
{ type: 'GET_OBJECTS', objects }, // I expected to see this object on store.getActions()[1]
];
return store.dispatch(postObjectAsync(object))
.then(() => {
store.getActions().should.deep.equal(expectedActions);
// AssertionError: expected [ Array(1) ] to deeply equal [ Array(2) ]
});
});
});
I expected store.getActions() to contain an array with both the GET_OBJECTS and ADD_OBJECT actions inside it but it only contains the ADD_OBJECT action
Can anybody weigh in?
Figured it out, the problem is not in the test,
// another async action that dispatches the previous async action
export const postObjectAsync = newObject =>
dispatch =>
axios.post(URL, newObject)
.then(() => { dispatch(addObject(newObject)); })
.then(() => { dispatch(getObjectAsync()); });
should be
// another async action that dispatches the previous async action
export const postObjectAsync = newObject =>
dispatch =>
axios.post(URL, newObject)
.then(() => {
dispatch(addObject(newObject));
return dispatch(getObjectAsync());
});
I just realized shouldn't use .then() on a synchronous function.
This post helped : How to handle two consecutive and dependant async calls in redux

Categories

Resources