I am updating a project from jest version 26 to jest version 27. As part of the update I had to switch from assertions on setTimeout to assertions on jest.spyOn(window, "setTimeout").
I want to define spy globally and reset it before each test, something like:
const timeoutSpy = jest.spyOn(window, "setTimeout");
beforeEach(() => {
jest.resetAllMocks();
});
This code doesn't work as I expected. Assertions for expect(timeoutSpy).toHaveBeenCalledTimes(n) fail due to mismatch of expected (n) and received (0) number of calls.
What is the correct way to reset a globally defined timeoutSpy before each test?
Thank you.
You should use jest.restoreAllMocks().
Restores all mocks back to their original value. Equivalent to calling .mockRestore() on every mocked function. Beware that jest.restoreAllMocks() only works when the mock was created with jest.spyOn; other mocks will require you to manually restore them.
Using jest.resetAllMocks(); in the beforeEach should be sufficient. The code below can be used to prove it:
function callTimeout() {
setTimeout(() => {
console.log('hello');
})
}
const timeoutSpy = jest.spyOn(window, 'setTimeout');
describe("test timeout", () => {
beforeEach(() => {
jest.resetAllMocks();
});
it("test 1", () => {
callTimeout();
callTimeout();
expect(timeoutSpy).toHaveBeenCalledTimes(2);
})
it("test 2", () => {
callTimeout();
callTimeout();
expect(timeoutSpy).toHaveBeenCalledTimes(2);
})
});
I had to test testEnvironment property in jest.config.js file to jsdom for window variable to work. However you can replace window to global for it to work with the default as node.
Related
I am trying to learn how to rxjs marble test using testScheduler. When trying to test a behavior subject getter and setter I get the error "Expected spy setVersatilityRowOptions to have been called with:[ VersatilityHeaderRowOptions({ onlyShowCertifiedOperators: true, showOffTeamOperators: false })] but it was never called."
Here is what my code looks like
My Service Functions
setVersatilityRowOptions(versatilityRowOptions: VersatilityHeaderRowOptions): void {
this._versatilityRowOptions.next(versatilityRowOptions);
}
getVersatilityRowOptions(): Observable<VersatilityHeaderRowOptions> {
return this.$versatilityRowOptions;
}
My Component Function I'm testing
updateVersatility(): void {
this.versatilityApiService.getVersatilityRowOptions().subscribe(versatilityRowOptions => {
this.versatilityApiService.setVersatilityRowOptions(versatilityRowOptions);
});
}
My Test File (I have mocked the object, the service, and the services functions in my spec file as well. Additionally, the service has been correctly injected into my spec file).
const testScheduler = new TestScheduler((actual, expected) => {
expect(actual).toEqual(expected);
});
describe('updateVersatility()', () => {it('should update behavior subject', () => {testScheduler.run(({cold}) => {const source1$ = cold('--(x|)', {x: mockVersatilityHeaderRowOptions});spyOn(component['versatilityApiService'],'getVersatilityRowOptions').and.returnValue(source1$);spyOn(component['versatilityApiService'], 'setVersatilityRowOptions');
component.updateVersatility();
getTestScheduler().flush();
fixture.detectChanges();
expect(
component['versatilityApiService'].getVersatilityRowOptions
).toHaveBeenCalled();
expect(component['versatilityApiService'].setVersatilityRowOptions).toHaveBeenCalledWith(mockVersatilityHeaderRowOptions);
});
I tried to run the test Scheduler and thought that the observable would flush and I could expect the setter to be called but it was not.
Recently I am learning to test React with jest and enzyme, It seems hard to understand what a unit test is it, my code
import React from "react";
class App extends React.Component {
constructor() {
super();
this.state = {
value: ""
};
this.handleChange = this.handleChange.bind(this);
}
handleChange(e) {
const value = e.target.value;
this.setState({
value
});
}
render() {
return <Nest value={this.state.value} handleChange={this.handleChange} />;
}
}
export const Nest = props => {
return <input value={props.value} onChange={props.handleChange} />;
};
export default App;
and my test
import React from "react";
import App, { Nest } from "./nest";
import { shallow, mount } from "enzyme";
it("should be goood", () => {
const handleChange = jest.fn();
const wrapper = mount(<App />);
wrapper.find("input").simulate("change", { target: { value: "test" } });
expect(handleChange).toHaveBeenCalledTimes(1);
});
IMO, the mocked handleClick will intercept the handleClick on App,
if this is totally wrong, what's the right way to use mock fn and test the handleClick be called.
Another: I search a lot, read the similar situations, seem like this iscontra-Unit Test,
Probably I should test the two component separately, I can test both components,
test the
<Nest value={value} handleChange={handleChange} />
by pass the props manually, and then handleChangeinvoked by simulate change
it passed test.
but how can I test the connection between the two?
I read
some work is React Team's Work
...
I don't know which parts I have to test in this case, and Which parts react already tested and don't need me to test. That's confusing.
You should take the path of testing the Nest component in isolation first, passing your mocked handleChange as a prop, to verify that input changes are being propagated.
If you want to test the state part, then you can get the instance of your App class from enzyme and call that method directly:
it("should update the Nest value prop when change is received", () => {
const wrapper = mount(<App />);
const instance = wrapper.instance()
instance.handleChange( { target: { value: "test" } })
const nestComponent = wrapper.find("Nest").first()
expect(nestComponent).prop('value').toEqual('test');
});
This a very very basic, almost not needed to test piece of code, but it will get your test coverage up if that's what you're after.
Doc for instance: http://airbnb.io/enzyme/docs/api/ReactWrapper/instance.html
If you want to test for the connection. From what I see, the nest component is a child component inside the App component. You could test that <App /> contains `.
describe('<App />', () => {
it('should contain a nest component', () => {
const wrapper = mount(<App />);
expect(wrapper.find(<Nest />)).toHaveLength(1);
});
});
Secondly, since the onChange event on the nest component updates the state in the App component, you can also test for state changes since its a behavior you expect.
it('should update state', () => {
//find input and simulate change with say {value: 'new value'} and then
expect(wrapper.state().value).toBe('newValue');
});
I hope this helps.
Jest provides a way to mock functions as described in their docs
apiGetMethod = jest.fn().mockImplementation(
new Promise((resolve, reject) => {
const userID = parseInt(url.substr('/users/'.length), 10);
process.nextTick(
() => users[userID] ? resolve(users[userID]) : reject({
error: 'User with ' + userID + ' not found.',
});
);
});
);
However these mocks only seem to work when the function is called directly in a test.
describe('example test', () => {
it('uses the mocked function', () => {
apiGetMethod().then(...);
});
});
If I have a React Component defined as such how can I mock it?
import { apiGetMethod } from './api';
class Foo extends React.Component {
state = {
data: []
}
makeRequest = () => {
apiGetMethod().then(result => {
this.setState({data: result});
});
};
componentDidMount() {
this.makeRequest();
}
render() {
return (
<ul>
{ this.state.data.map((data) => <li>{data}</li>) }
</ul>
)
}
}
I have no idea how to make it so Foo component calls my mocked apiGetMethod() implementation so that I can test that it renders properly with data.
(this is a simplified, contrived example for the sake of understanding how to mock functions called inside react components)
edit: api.js file for clarity
// api.js
import 'whatwg-fetch';
export function apiGetMethod() {
return fetch(url, {...});
}
You have to mock the ./api module like this and import it so you can set the implemenation of the mock
import { apiGetMethod } from './api'
jest.mock('./api', () => ({ apiGetMethod: jest.fn() }))
in your test can set how the mock should work using mockImplementation:
apiGetMethod.mockImplementation(() => Promise.resolve('test1234'))
In case the jest.mock method from #Andreas's answer did not work for you. you could try the following in your test file.
const api = require('./api');
api.apiGetMethod = jest.fn(/* Add custom implementation here.*/);
This should execute your mocked version of the apiGetMethod inside you Foo component.
Here is an updated solution for anyone struggling with this in '21. This solution uses Typescript, so be aware of that. For regular JS just take out the type calls wherever you see them.
You import the function inside your test at the top
import functionToMock from '../api'
Then you indeed mock the call to the folder outside of the tests, to indicate that anything being called from this folder should and will be mocked
[imports are up here]
jest.mock('../api');
[tests are down here]
Next we mock the actual function we're importing. Personally I did this inside the test, but I assume it works just as well outside the test or inside a beforeEach
(functionToMock as jest.Mock).mockResolvedValue(data_that_is_returned);
Now here's the kicker and where everyone seems to get stuck. So far this is correct, but we are missing one important bit when mocking functions inside a component: act. You can read more on it here but essentially we want to wrap our render inside this act. React testing library has it's own version of act. It is also asynchronous, so you have to make sure your test is async and also define the destructured variables from render outside of it.
In the end your test file should look something like this:
import { render, act } from '#testing-library/react';
import UserGrid from '../components/Users/UserGrid';
import { data2 } from '../__fixtures__/data';
import functionToMock from '../api';
jest.mock('../api');
describe("Test Suite", () => {
it('Renders', async () => {
(functionToMock as jest.Mock).mockResolvedValue(data2);
let getAllByTestId: any;
let getByTestId: any;
await act(async () => {
({ getByTestId, getAllByTestId } = render(<UserGrid />));
});
const container = getByTestId('grid-container');
const userBoxes = getAllByTestId('user-box');
});
});
Another solution to mock this would be:
window['getData'] = jest.fn();
I just want to test if myAwesome action is dispatched when my App.vue component is created(). Is this something you would test? I'm using Jasmine for these tests. Any help would be awesome!
App.js
describe('when app is created()', () => {
it('should dispatch myAwesomeAction', (done) => {
const actions = {
myAwesomeAction() {
console.log('myAwesomeAction')
// commit()
}
}
const state = {
myAwesomeAction: false
}
const mutations = {
LOAD_SUCCESS(state) {
state.myAwesomeAction = true
}
}
const options = {
state,
mutations,
actions
}
const mockStore = new Vuex.Store(options)
spyOn(mockStore, 'dispatch')
const vm = new Vue({
template: '<div><component></component></div>',
store: mockStore,
components: {
'component': App
}
}).$mount()
Vue.nextTick(() => {
expect(mockStore.dispatch).toHaveBeenCalled()
expect(mockStore.dispatch).toHaveBeenCalledWith('myAwesomeAction')
done()
})
})
})
Errors:
1) should dispatch myAwesomeAction
App
Expected spy dispatch to have been called.
webpack:///src/views/App/test/App.spec.js:49:6 <- index.js:50916:50
webpack:///~/vue/dist/vue.esm.js:505:15 <- index.js:3985:24
nextTickHandler#webpack:///~/vue/dist/vue.esm.js:454:0 <- index.js:3934:16
Expected spy dispatch to have been called with [ 'loadOrganisation' ] but it was never called.
webpack:///src/views/App/test/App.spec.js:50:54 <- index.js:50918:54
webpack:///~/vue/dist/vue.esm.js:505:15 <- index.js:3985:24
nextTickHandler#webpack:///~/vue/dist/vue.esm.js:454:0 <- index.js:3934:16
The thing is, you are trying to unit test a store from a component, so there's a little bit of a problem when you are mocking some elements and relying in true functionality in other elements. I'm no expert in vuex, I had a similar problem trying to spy on a store action and call a component's method (can't remember what the problem was, i remember i did waste half a day with it).
My suggestion: test component as unit, then test store module as unit, that means in your app component you can spy
spyOn(vm, 'myAwesomeAction');
Vue.nextTick(() => {
expect(vm.myAwesomeAction).toHaveBeenCalled()
done()
});
(that is, check if initializing your component, your method that calls the store action is called, in my example myawesomeaction will be a mapAction name in the methods object)
And then, you can unit test your store, and check that if you call myawesomeaction the mutation on that component will occur
check the test action helper here: https://vuex.vuejs.org/en/testing.html
I'm working on my unit test cases for Angular 2 with Karma, I got stuck with one of a function where I run the test for below line
expect(component.subscribeToEvents()).toBeTruthy();
and I view my coverage code, the lines inside the test file seems not covering anything inside the subscribe. I have tried using MockBackend in mocking the api call inside a function on service but I'm not sure how to do the mocking on a subscribed object, can somebody please help me?
The below is in test.component.ts
subscribeToEvents() {
this.subscription = this.czData.$selectedColorZone
.subscribe(items => {
this.resourceLoading = true;
if (!this.resourceData || (this.resourceData && this.resourceData.length === 0)) {
this.settings.layout.flypanel.display = false;
this.getAllResources(this.pagination.start, this.pagination.size);
}
else {
this.pagination.start = 1;
this.pagination.end = this.pagination.size;
this.getAllResources(1, this.pagination.size);
this.settings.layout.flypanel.display = true;
}
});
return true;
}
The screenshot of the coverage code
You can't do this, as the subscription is resolved asynchronously. So the synchronous test completes before the async task is resolved.
If all you want is coverage, you can just make the test async. This will cause the Angular test zone to wait until the async task is resolved, before completing the test
import { async } from '#angular/core/testing';
it('..', async(() => {
component.subscribeToEvents();
}))
You can't try to expect anything here, as there is no callback hook for when the task is resolved. So this is really a pointless test. It will give you coverage, but you aren't actually testing anything. For instance, you might want to test that the variables are set when the subscription is resolved.
Based on the code provided, what I would do instead is just mock the service, and make it synchronous. How can you do that? We you can make the mock something like
class CzDataSub {
items: any = [];
$selectedColorZone = {
subscribe: (callback: Function) => {
callback(this.items);
}
}
}
Then just configure it in the test
let czData: CzDataStub;
beforeEach(() => {
czData = new CzDataStub();
TestBed.configureTestingModule({
providers: [
{ provide: CzData, useValue: czData }
]
})
})
Now in your tests, you don't need to make it async, and you can provide any value you want by just setting the items property on the mock, and subscriber will get it
it('..', () => {
czData.items = something;
component.subscribeToEvents();
expect(component.settings.layout.flypanel.display).toBe(false);
})
UPDATE
I think I was half asleep when I wrote this post. One of the above statements is incorrect
You can't try to expect anything here, as there is no callback hook for when the task is resolved.
This is not completely true. This is what fixture.whenStable() is for. For instance if this is your service
class CzData {
_value = new Subject<>();
$selectedColorZone = this._value.asObservable();
setValue(value) {
this._value.next(value);
}
}
Then this is how you would make the test work
let czData: CzData;
let fixture: ComponentFixture<YourComponent>;
let component: YourComponent;
beforeEach(() => {
TestBed.configureTestingModule({
providers: [ CzData ],
declarations: [ YourComponent ]
});
fixture = TestBed.createComponent(YourComponent);
component = fixture.componentInstance;
czData = TestBed.get(czData);
})
it('..', async(() => {
component.subscribeToEvents();
czData.setValue(somevalue);
fixture.whenStable().then(() => {
expect(component.settings.layout.flypanel.display).toBe(false);
})
}))
We use fixture.whenStable() to to wait for the async tasks to complete.
This is not to say that using the mock is wrong. A lot of the time, using the mock would be the way to go. I just wanted to correct my statement, and show how it could be done.
Consider how Angular Outputs are tested since they are subscribed to during testing: https://angular.io/guide/testing#clicking
it('should raise selected event when clicked (triggerEventHandler)', () => {
let selected: Hero;
comp.selected.subscribe((hero: Hero) => selectedHero = hero);
heroDe.triggerEventHandler('click', null);
expect(selectedHero).toBe(expectedHero);
});
So try:
const expectedItem = {}; // mock the expected result from 'subscribeToEvents'
it('should raise selected event when clicked (triggerEventHandler)', () => {
let selectedItem: any; // change to expected type
component.subscribeToEvents.subscribe((item: any) => selectedItem = item);
// fixture.detectChanges(); // trigger change detection if necessary here, depending on what triggers 'subscribeToEvents'
expect(selectedItem).toBe(expectedItem);
});