I'm in bit of a dead-end as I'm not sure how I should go about testing this specific "component". So basically, I have a controller component which is a top-level component. It doesn't take in any props, and it is rendered by a route component. The controller component has several functions, which some are passed into a child component and are triggered by event handlers.
Additionally, the controller component uses an API that is attached to the global window object. The API takes in a callback function which then will be called when you call certain methods on the API, after the methods have been run. Right now, I have no idea how I should try to test the controller. I have tested all child components and verified that everything works, but some of these functions within the controller component would be crucial to test that they actually do work.
const MyController = () => {
const [api, setApi] = useState(null)
useEffect(() => {
const globalApi = window.globalApi
setApi(globalApi)
init()
}, [])
function callBack(e) {
console.log(e)
}
function init() {
api.init(callBack)
}
function close() {
api.close()
}
return (
<MyComponent
close={close}
/>
)
}
Mock your api and see if its called
You can spy on your api call from global (if I read your code correctly). Then you can mock implement it.
const apiCall = jest.spyOn(global.globalApi, 'init').mockImplementation(jest.fn);
expect(apiCall).toHaveBeenCalled();
There's a couple of tests you can do. Check how many times its called, should be once for you. and check what arguments it was called with.
Note
The use of global in the spy. global refers to the window.
Extra tests
Beyond these tests I would suggest making a snap shot of a shallow render, just to make sure the render is always working.
Related
I have a button with "Download" text on it defined in ReactJS code. Now, I want to write a unit test to check that this function is getting called when this button is clicked. I wrote a unit test but it is not working.
import * as FileSaver from "file-saver"
it('File has to be saved when clicked on the "Download" button', () => {
jest.mock('file-saver', ()=>({saveAs: jest.fn()}));
fireEvent.click(component.getByText("Download"));
expect(FileSaver.saveAs).toBeCalled();
})
I'm getting this error:
Error: expect(received).toBeCalled()
Matcher error: received value must be a mock or spy function
Received has type: function
Received has value: [Function anonymous]
pointing to expect(FileSaver.saveAs).toBeCalled(); line.
What's wrong?
So, as I stated in the comment, you have to move jest.mock('file-saver', ()=>({saveAs: jest.fn()})) from the body of the test to the top of the file, just under the imports. The reason for it is actually answered in the documentation here, but to wrap it up:
In your test file you are using import statement which fires up at start, before any code has a chance to run. Then you try to mock file-saver, but it is already imported with real implementation, not mocked. If you instruct jest to mock module at top of the file it will automatically hoist jest.mock calls to the top of the module so your function exportToExcel will receive mocked file-saver instead of the real one.
But if you really want to mock file-saver in the body of the test for some strange reason you would need to mock file-saver, then include all modules in test which use file-saver, something like this:
it('File has to be saved when clicked on the Export button', () => {
jest.mock('file-saver', ()=> ({ saveAs: jest.fn() }));
const component = require('/path/to/tested/componetn/on/which/click/event/is/fired');
const FileSaver = require('file-saver');
fireEvent.click(component.getByText("Download"));
expect(FileSaver.saveAs).toBeCalled();
})
i am trying to write a unit test to test if the correct method is called when the action is dispatched. Here are my files:
actions.js
export const foo = () => ({type: 'fooAction'})
services.js
function* watchFoo(){
yield takeEvery('fooAction', setFoo);
}
function setFoo() { console.log('foo'); }
And now in my spec file, i am test the action to see if setFoo is called.
tests/services.js
import configureStore from 'redux-mock-store';
describe('test setFoo', () => {
const mockStore = configureStore([]);
const store = mockStore([]);
store.dispatch(actions.foo());
const storeActions = store.getActions();
expect(storeActions).toEqual([action]); // this is true
})
When i try to debug setFoo, setFoo method is not called. I did set the spy to setFoo (removed that in the code above) which is not getting called. I also tried creating a mock for services and then spyOn the mocked service. Even then the setFoo is not called. To be clear, the issues are:
Why is my setFoo method not called?
How to write the unit test for this approach?
We can't tell you why it's not working unless you post all the relevant code. Since you did not show any usage of watchFoo in your posted code, it's impossible to tell you why it's not working. Your question also does not mention what takeEvery is and how it's supposed to interact with your code. Are you using redux-saga?
Just test watchFoo() directly*.
The test you are writing is testing the integration of services.js with redux-mock-store, which is not a helpful test.
If you want an integration test that reflects your running code, you would need to test with the real store.
But you specifically mentioned a unit test, so you don't need a store at all.
*If you're using redux-saga, which you didn't mention, but I'm guessing that's where your takeEvery comes from, the recommended way to test is described here: https://redux-saga.js.org/docs/advanced/Testing.html. No mocking required.
I want to connect to the coincap.io public socket.io API. I have everything set up but don't know where in my component to put the socket.io code. Does it go in the constructor or componentWillMount or where? It's socket.io so it obviously always needs to be open so where in a component would that go? Here is the code I need to inject somewhere into my component:
this.socket = io.connect('http://socket.coincap.io');
this.socket.on('connect', function(tradeMsg) {
console.log("It worked");
});
Does it go in the constructor or componentWillMount?
Check these answers for details about this:
Can I call APIs in componentWillMount in React?
Why do the React docs recommend doing AJAX in componentDidMount, not componentWillMount?
Where in my component to put the socket.io code?
Use componentDidMount lifecycle method for, it will triggered only once after the component has been mounted successfully, we should write all kind of network calls inside this.
As per DOC:
componentDidMount() is invoked immediately after a component is
mounted. Initialization that requires DOM nodes should go here. If you
need to load data from a remote endpoint, this is a good place to
instantiate the network request. Setting state in this method will
trigger a re-rendering.
Write it like this:
componentDidMount(){
this.socket = io.connect('http://socket.coincap.io');
this.socket.on('connect', function(tradeMsg) {
console.log("It worked");
});
}
You can add socket code into 'componentDidMount`. refer link
componentDidMount(){
this.socket = io.connect('http://socket.coincap.io');
this.socket.on('connect', function(tradeMsg) {
console.log("It worked");
});
}
I've been developing in React for a while for my work, but recently I was requested to get some applications to ~100% test coverage using Istanbul. I've wrote over 160 tests for this application alone in the past few days, but I haven't been able to cover certain parts of my code. Im having the most trouble covering AJAX calls, setTimeout callbacks, and component methods that require another component to operate properly.
I've read several SO questions to no avail, and I believe that is because I'm approaching this incorrectly. I am using Enzyme, Chai assertions, Mocha, Istanbul coverage, sinon for spies, and was considering nock since I cant get sinon fakeServer working.
Here is the component method in question:
_getCategoriesFromServer() {
const _this = this;
sdk.getJSON(_this.props.sdkPath, {
itemsperpage: 10000
}).done(function(data) {
_this.setState({
isLoaded: true,
categories: _this.state.categories.concat(data)
});
});
}
Here is the test for that component:
it('should call _getCategoriesFromServer', () => {
sinon.spy(CategoryTree.prototype, '_getCategoriesFromServer');
wrapper = mount(<CategoryTree {...props} />);
expect(CategoryTree.prototype._getCategoriesFromServer.calledOnce).to.be.true;
});
The sdk is just a module that constructs a jQuery API call using getJSON.
My test is covering the function call, but its not covering the .done callback seen here:
So my question is, how can I properly test the .done?
If anyone has an article, tutorial, video, anything that explains how to properly test component methods, I would really appreciate it!
Second question is, how can I go about testing a method that gets passed down as a prop to a child component? With the testing coverage requirement I have to have that method tested, but its only purpose is to get passed down to a child component to be used as an onClick. Which is fine, but that onClick is dependent on another AJAX call returning data IN the child component.
My initial impulse was to just use enzymes .find to locate that onClick and simulate a click event, but the element with that onClick isn't there because the AJAX call didn't bring back data in the testing environment.
If you've read this far, I salute you. And if you can help, I thank you!
You could use rewire(https://github.com/jhnns/rewire) to test your component like this:
// let's said your component is ListBox.js
var rewire = require("rewire");
var myComponent = rewire("../components/ListBox.js");
const onDone = sinon.spy()
const sdkMock = {
getJSON (uri, data) {
return this.call('get', uri, data);
},
call: function (method, uri, data) {
return { done: function(){ onDone() } }
}
};
myComponent.__set__("sdk", sdkMock);
and finally you will test if the done function get called like this:
expect(onDone.calledOnce)to.be.true
With this should work as expected. If you need more options you could see all the options of rewire in GitHub.
BABEL
If you are using babel as transpiler you need to use babel-plugin-rewire(https://github.com/speedskater/babel-plugin-rewire) you could use it like this:
sdk.js
function call(method, uri, data) {
return fetch(method, uri, data);
}
export function getJSON(uri, data) {
return this.call('get', uri, data);
}
yourTest.js
import { getJSON, __RewireAPI__ as sdkMockAPI } from 'sdk.js';
describe('api call mocking', function() {
it('should use the mocked api function', function(done) {
const onDone = sinon.spy()
sdkMockAPI.__Rewire__('call', function() {
return { done: function(){ onDone() } }
});
getJSON('../dummy.json',{ data: 'dummy data'}).done()
expect(onDone.calledOnce)to.be.true
sdkMockAPI.__ResetDependency__('call')
})
})
I'm trying to test a click on a react component uszing Enzyme + Sinon
var stub = sinon.stub(Comp.prototype, 'save', function() { });
let wrapper = shallow(
<Comp/>
);
wrapper.find('.btn-header').simulate('click');
sinon.assert.called(stub);
Comp.prototype.refineOnClick.restore();
My Comp component has a save function that throws an exception
save: function () {
throw('error');
}
When I run the test, I expect no errors to be thrown and the empty function in the stub to fire - but it doesn't. The actual function inside the component is fired and not the empty stub.
You can access (and therefore stub) functions on your enzyme wrapper by using it's instance. So to test your component's save function, you do the following:
const wrapper = shallow(<Comp />)
sinon.stub(wrapper.instance(), 'save')
wrapper.find('.btn-header').simulate('click')
expect(wrapper.instance().save).to.have.been.called
Note, I'm using sinon-chai for the .to.have.been.called syntax.
One of the principles of unit testing is, that you shouldn't fake internals of unit under test. It just makes tests less readable and maintainable. Method save is obviously internal to Comp and thus shouldn't be faked.
It would be OK if you would pass it as parameter into that component:
var stub = sinon.stub();
let wrapper = shallow(
<Comp onSave={stub} />
);
wrapper.find('.btn-header').simulate('click');
sinon.assert.called(stub);