just starting to dive into testing world and one thing confuses me. I have class like this:
class TestClass {
static staticMethod () {
methodOne();
methodTwo();
}
and test like this:
test('should call methodOne function', () => {
TestClass.staticMethod();
expect(methodOne).toHaveBeenCalled();
});
test('should call methodTwo function', () => {
Test.staticMethod();
expect(methodTwo).toHaveBeenCalled();
expect(methodTwo).toHaveBeenCalledTimes(1);
});
Jest throws an error, that says methodTwo has been called two times instead of one. I Figured, it's because I'm running two tests that calls class static method two times (one time in first test, and second time in second test), hence methodTwo is called two times.
So my question, is it possible somehow to isolate those tests ? When I'm running test one (calling some class method) it shouldn't influence other test results.
Thanks!
You're right, by default Jest spies keep their state through your different tests.
To reset them, I personally use :
afterEach(() => {
jest.clearAllMocks()
})
see https://facebook.github.io/jest/docs/en/jest-object.html#jestclearallmocks for more information
Related
I have a component with some parts that are conditionally rendered based on some config on the window object. This all works, I can confirm this manually.
I have this sort of test setup, but I have simplified it somewhat:
it('renders both by default', () => { // PASS
const Comp = getComponent();
render(<Comp />);
expect(screen.getByTestId('one')).toBeInTheDocument();
expect(screen.getByTestId('two')).toBeInTheDocument();
})
it.each([
{ testId: 'one', otherTestId: 'two'},
{ testId: 'two', otherTestId: 'one' },
])('renders $testId, not $otherTestId', (testId, otherTestId) => { // FAIL
delete window.config[otherTestId]; // this works
const Comp = getComponent();
render(<Comp />);
expect(screen.getByTestId(testId)).toBeInTheDocument();
expect(screen.getByTestId(otherTestId)).not.toBeInTheDocument();
})
But I am getting this error:
Expected done to be called once, but it was called multiples times. Reason 'two'.
Which is not something I've ever seen before. None of my tests that are running here are async. The component isn't async. I'm not using done anywhere near this.
What's happening?
You need to destructure your test variables
Jest is expecting testId to be an object containing all your test variables, and otherTestId to be the function done.
I'm not sure why it thinks it's called multiple times, but making the first argument:
{ testId, otherTestId }
Works around this, and matches the documentation correctly.
Alternatively, follow this answer for replacing jest's for loop with the native one, which is a better approach in many ways.
I am trying to test the methods of my Vue components; the test is seems working fine. But the problem is that it's giving a depreciation warning in the console.
I believe that the vue-test-utils teams would be removing the setMethods and methods property in their next major release.
My problem is that there are no alternative ways to achieve the same things provided for both setMethods and methods property.
Just they suggested a warning:
To stub a complex method extract it from the component and test it in isolation. Otherwise, the suggestion is to rethink those tests.
My Question: How can we extract the method and test the functionalities of the method clicked from the component level or not?
Below is my simple example where I mocked a method and checking if it gets called inside the component when triggered click.
const downloadStub = jest.fn()
const wrapper = mount(Documents, {
methods: { downloadNow: downloadStub },
})
it('check download button clicked and calling downloadNow method', () => {
wrapper.find('.download-button').trigger('click')
expect(downloadStub).toBeCalled()
})
Note: No issues in running the above code; I want to know the alternative ways to achieve the same result and avoid the warning?
After some research, I found one solution that works the way I expected. I am able to achieve this, without using any methods properties and setMethods.
This way, I got rid of the warning message methods property is deprecated and will be removed.
const wrapper = mount(Documents, {
})
it('check download button clicked and calling downloadNow method', () => {
const downloadSpy = jest.spyOn(wrapper.vm, 'downloadNow')
wrapper.find('.download-button').trigger('click')
expect(downloadSpy).toBeCalled()
})
I use the jest.spyOn method and pass the wrapper.vm and method name.
Other solutions are also welcome.
So after I read common tips and watched the video about testing provided by Vue.js I decided to test only behaviors of my components. But I maybe misunderstand the concept. Here is the component:
<template>
<es-btn
class="a"
round
outline
color="primary"
#click="skipScan"
>
Skip Upload
</es-btn>
</template>
<script>
export default {
data: () => ({
uploadScanningDialog: false,
}),
methods: {
// public functions here which will be tested
skipScan() {
hideDialog();
triggerSkipScan();
},
},
}
// private functions here as vue.js suggests
function hideDialog() {
this.uploadScanningDialog = false;
}
....
</script>
I want to test the behavior:
it('should hide itself and show next dialog when Scan Button is clicked', () => {
const data = {
uploadScanningDialog: true,
};
wrapper.setData(data);
expect(wrapper.vm.uploadScanningDialog).toBeTruthy();
wrapper.find('.a').trigger('click');
expect(wrapper.vm.uploadScanningDialog).toBeFalsy();
});
So here are the questions and errors:
Should I do test this way, by triggering the action instead of calling the method itself? Because I used to test by calling the method and expecting some results, not triggering the component who calls the method
Shouldn't I test the private functions? Because my test tries to call real method, so the test I share gets error. I will mock them but I'm not sure how to continue
Generally it is better to unit test your code through its public api, where possible.
This improves maintainability, as:
It allows you to refactor any private functions, without having to update your unit tests.
If you make changes to your private functions, you can then assert that you haven't broken anything by running your unit tests against your public api.
Whether you run your unit tests by calling a public method or triggering a DOM event is a matter of choice.
The latter gives greater test coverage as you are also testing if you have wired up the event to the event handler function correctly in your template; but it is slightly less maintainable, as if you change your template you may have to update your tests.
Context
My issue is that my ngOnInit function calls many sub functions asynchronously by using Firestore to populate a form from one document as well as subscribing to various collections to display lists of data. This gets very tedious to test as I then have to ensure things are subscribing properly and values are being returned in the correct order.
According to the Angular docs:
However, it's often more productive to explore the inner logic of application classes with isolated unit tests that don't depend upon Angular. Such tests are often smaller and easier to read, write, and maintain.
Which in my opinion would make preventing the ngOnInit (through a spy) a valid way of
Isolating tests and
Reducing complexity when initializing the tests
You can then explicitly call each function separately and only test what is needed, instead of having ngOnInit calling the functions (and possible you as well).
This is exactly how I have gone about it so far:
Component
ngOnInit() {
console.log('init');
this.setup();
}
setup() {
this.firebaseService.setup(_ => {
this.checkNewOrEdit();
});
}
...
Spec
...
beforeEach(() => {
fixture = TestBed.createComponent(Component);
component = fixture.componentInstance;
spyOn(component, 'ngOnInit');
fixture.detectChanges();
});
it('should be created', () => {
fixture.whenStable().then(_ => {
expect(component).toBeTruthy();
});
});
it('should call checkNewOrEdit() on setup()',
() => {
spyOn(component, 'checkNewOrEdit');
callbackValue = 'success';
component.setup();
expect(component.checkNewOrEdit).toHaveBeenCalled();
}
);
Which is working fine for me so far.
Is this an acceptable solution? Or are there better ways of going about this?
I had problems further down the line in terms of variables not being set previously.
This solution does work but it just adds further complexity further down the line. I would recommend solving the problem initially, then you don't have to worry about how and where variables where set later
I solved my problems by editing my firebase mocks to include all the collections and docs I subscribe to throughout the initialisation chain. I think I must have forgotten a a doc or collection somewhere.
I have multiple situations in code where we pass a function as an argument when creating an object (like so in onPress):
<Touchable
onPress={() => Linking.openURL(formatUrl(url))}
noContainer={true}>
{children}
</Touchable>
When it comes down to it, this is a snippet in a larger component getting rendered. As it's not actually a method of the component, I'm not especially interested in testing it here - it will get tested as part of another component if needed.
However, the code coverage report comes back with an indication that the function is uncovered.
Is there a way to satisfy this coverage - either by testing this function, or ignoring all functions passed in such a way (anonymously, as arguments)?
Assuming you are using Jest, you can set your Jest mock to call the argument that it receives, so:
const componentProps = {
onPress: jest.fn(func => func());
}
So the mocked onPress function, in this case, will receive your anonymous function and call it. Coverage will show that the anonymous function was called.
Hope it helps :)
First, I'd like to advise against exempting lines from coverage, especially if you're being paid to write this code and unit test it.
One thing I've found that works is to write a void function that calls your function with your desired arguments.
if you have:
onClick={ () => function(argument) }
and Jest isn't covering it, try:
const functionWithArgs = () => function(argument)
onClick={functionWithArgs}
I use this a lot for modals:
const [isModalOpen, setModalOpen] = React.useState(false)
const toggleModal = () => setModalOpen(!isModalOpen)
onClick={toggleModal}
But the same principle can be applied to a much more complex function. And it works!