Reputation: 4696
I'm struggling a bit to find a solution on how to test this exported function with jest.
export const scrollToError = () => {
setTimeout(() => {
const hasErrorElement = jQuery('.has-error');
if (!hasErrorElement.length) return;
jQuery('html,body').animate({
scrollTop: hasErrorElement.offset().top - 50,
}, 'slow');
}, 400);
};
I imported it in my test file and tried to start it:
import { scrollToError } from './utils';
describe('Utils', () => {
it('should scroll to error', () => {
const result = scrollToError();
expect(result).toBe(true); //added this just to force an error and got result as undefined
});
});
Could anyone give me any tips on how to test code with these dependencies?
Upvotes: 0
Views: 793
Reputation: 4696
I finally managed to find a proper solution. I wrote three test cases for it:
jest.useFakeTimers();
describe('utils', () => {
afterEach(() => {
document.body.innerHTML = '';
});
it('ScrollToError - should run the settimeout for 400 ms', () => {
scrollToError();
expect(setTimeout).toHaveBeenCalledTimes(1);
expect(setTimeout).toHaveBeenCalledWith(expect.any(Function), 400);
});
it('ScrollToError - should scroll to error', () => {
document.body.innerHTML = formStep1ErrorMock;
window.setTimeout = fn => fn();
const result = scrollToError();
expect(result).toBe(true);
});
it('ScrollToError - should do nothing as has no errors', () => {
document.body.innerHTML = formStep1Mock;
window.setTimeout = fn => fn();
const result = scrollToError();
expect(result).toBe(true);
});
});
So basically, first I check if the setTimeout was called with the proper amount of seconds (not that it's important).
Then I mock the setTimeout by doing this window.setTimeout = fn => fn();
so it runs without waiting for the delay. I also mock the html with the proper details I need it to have.
And finally, I just cover another scenario.
PS: I added a return true
statement to the scrollToError method to make it simpler to have an expected result.
This way I achieved 100% coverage for this method.
Upvotes: 1
Reputation: 568
How are you using jQuery?
I mean, did you get it using npm or yarn? to mock node_modules you can follow this link: https://jestjs.io/docs/en/manual-mocks#mocking-node-modules
Otherwise, you will have to create a manual mock. You can see how to do it here: https://jestjs.io/docs/en/manual-mocks
Updated:
the simplest way is to override it, is while settting up your test at beforeXXX
method.
You can simply put something like window.JQuery = jest.fn();
this is the simplest mock ever but you will have to create the methods like animate
and other jquery related methods.
Having second thoughts here and looking to your function, if you mock jQuery what else left to be tested?
If you mock, you will be testing if your fn are doing the steps you defined here. Like check if the jQuery fn was called with .has-error
class or if animate
received the correct parameters.
This kind of test doesn't help you at all, it's just checking if it's following line by line your algorithm. The problem here, that you could do some refactorings like changing the .has-error
class name or the animate method by other improved one.
What you really need to change, if it's doing at the end what should be doing. Displaying the div or whatever that should be displayed. If you test that, regardless the way you refactor your code the test will check if the final solution still works and that what matters.
Was I clear? English is not my first language so, it may be a little bit confusing
Upvotes: 1
Reputation: 16754
scrollToError()
is asynchronous function and you can't invoke it and expect the result to be there immediately. You need to wait that amount of ms (400 in your case), before testing for it.
Asynchronous code is tested a bit differently in Jest: Testing Asynchronous Code. You can also take control over the timers or combine it all with the manual mocks and override jQuery itself.
Upvotes: 1