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.
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'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.
I've seen a lot of articles about how use async/await in your unit tests, but my need is the opposite.
How do you write a test for a method that uses async/await?
My spec is not able to reach any code after the 'await' line. Specifically, the spec fails in two ways.
1) HelloWorld.otherCall returns undefined instead of the return value I specify
2) HelloWorld.processResp never gets called
class HelloWorld {
async doSomething(reqObj) {
try {
const val = await this.otherCall(reqObj);
console.warn(val); // undefined
return this.processResp(val);
}
}
}
describe('HelloWorld test', function () {
let sut = new HelloWorld(); //gross simplification for demo purposes
describe('doSomething()', function () {
beforeEach(function mockInputs() {
this.resp = 'plz help - S.O.S.';
});
beforeEach(function createSpy() {
spyOn(sut, 'otherCall').and.returnValue( $q.resolve(this.resp) );
spyOn(sut, 'processResp');
});
it('should call otherCall() with proper arguments', function () {
//this test passes
});
it('should call processResp() with proper arguments', function () {
sut.doSomething({});
$rootScope.$apply(); //you need this to execute a promise chain..
expect(sut.processResp).toHaveBeenCalledWith(this.resp);
//Expected spy processResp to have been called with [ 'plz help SOS' ] but it was never called.
});
});
});
Running angular 1.5 and jasmine-core 2.6.
The .then of a promise is overloaded to handle either promises or values, and await is syntactic sugar for calling then.
So there is no reason your spy would be required to return a promise, or even a value. Returning at all, even if undefined, should trigger the await to fire, and kick off the rest of your async function.
I believe your problem is that you are not waiting for the doSomething promise to resolve before trying to test what it did. Something like this should get you more in the ballpark.
it('should call processResp() with proper arguments', async function () {
await sut.doSomething({});
// ...
});
Jasmine has Asynchronous Support. You can probably find a solution that way.
Personally, I think you should not test such methods at all.
Testing state means we're verifying that the code under test returns the right results.
Testing interactions means we're verifying that the code under test calls certain methods properly.
At most cases, testing state is better.
At your example,
async doSomething(reqObj) {
try {
const val = await this.otherCall(reqObj);
return this.processResp(val);
}
}
As long as otherCall & processResp are well covered by unit tests your good.
Do something should be covered by e2e tests.
you can read more about it at http://spectory.com/blog/Test%20Doubles%20For%20Dummies
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')
})
})
Whilst attempting to run integration tests with Angular 2 and Karma test runner the following issue became clear. A test was always passing even when it should have been failing.
The issue occurs when the expect() method is placed inside the subscribe() method of an Observable.
The need to do this arose as the test would subscribe to the Observable and then continue processing the rest of the test before the Observable has finished executing.
However, placing the expect within the subscribe() method automatically causes the test to pass even when there are very obvious syntax errors:
it('should pass or fail', inject([Service], (_service : Service) => {
let result = _service.returnObservable();
result.subscribe((sfasdasdaa23231gr) => {
expect(r.isAfhahzdzd vailable).not.35q6w623tyrg /.0824568sfn toBe(truDDIDIDIDDIe);
});
}));
the previous code passes, but how? there are syntax errors everywhere. Does anyone know where this issue lies? In the testing or in the subscribe() method?
Because it's asynchronous processing, you should add the async method:
it('should pass or fail', async(inject([Service], (_service : Service) => {
let result = _service.returnObservable();
result.subscribe((sfasdasdaa23231gr) => {
expect(r.isAfhahzdzd vailable).not.35q6w623tyrg /.0824568sfn toBe(truDDIDIDIDDIe);
});
})));