I am new to JavaScript testing and currently trying to write some test cases for a store (just an ES6 class) I created. I am using Jest as this is what we usually use for React projects, although here I am not testing a React Component but just a class wrapping a functionality.
The class I am testing extends another class, and has various methods defined in it. I want to test these methods (whether they are called or not), and also whether the properties declared in the class change as and when the corresponding class methods are called.
Now I have read about mocking functions, but from what I understand, they can only do checks like how many times a function is called, but can't replicate the functionality. But in my case, I need the functionality of the methods because I will be checking the class member values these methods change when called.
I am not sure if this is the right approach. Is it wrong to test functions in Jest without mocking? And inferentially, to test the internal workings of functions? When do we mock functions while testing?
The issue I am facing is that the project I am working on is a large one where there are multiple levels of dependencies of classes/functions, and it becomes difficult to test it through Jest as it will need to go through all of them. As I am using alias for file paths in the project, Jest throws errors if it doesn't find any module. I know its possible to use Webpack with Jest, but many of the dependent classes/functions in the code are not in React, and their alias file paths are not maintained by Webpack.
import { getData } from 'service/common/getData';
class Wrapper extends baseClass {
someVariable = false;
payload = null;
changeVariable() {
this.someVariable = true;
}
async getData() {
super.start();
response = await fetchData();
this.payload = response;
super.end();
}
}
This is a small representation of the actual code I have. Can't post the entire class here as I am working on a remote machine. Basically, I want to test whether changeVariable gets called when invoked, and whether it successfully changes someVariable to true when called; and similarly, check the value of payload after network request is complete. Note that fetchData is defined in some other file, but is critical to testing getData method. Also the path used here (service/common/getData) for importing getData is not the absolute path but an alias NOT defined in Webpack, but somewhere else. Jest can't resolve getData because of this. I will not have to worry about this if I mock getData, but then I will not be able to test its functionality I believe.
#maverick It's perfectly okay to test your class methods using jest. Check the code example in the link -
https://repl.it/repls/ClumsyCumbersomeAdware
index.js
class Wrapper {
constructor(){
this.someVariable = false;
}
changeVariable(){
this.someVariable = true;
}
getData(){
return new Promise(resolve => resolve('some data'));
}
}
module.exports = Wrapper;
index.test.js
const Wrapper = require('./index');
const wrapper = new Wrapper();
describe('Wrapper tests', () => {
it('should changeVariable', () => {
wrapper.changeVariable();
expect(wrapper.someVariable).toBe(true);
});
it('should get some data', () => {
wrapper.getData().then( res => expect(res).toBe('some data'));
});
});
This is a very simplistic example and in real life the async calls are much more complicated and dependent of 3rd party libraries or other project modules. In such cases it makes sense to have all the dependencies injected in out class and then mocked individually. For Example -
class GMapService {
constructor(placesApi, directionApi){
this.placesApi = placesApi;
this.directionApi = directionApi;
}
getPlaceDetails(){
this.placesApi.getDetails('NYC');
}
getDirections(){
this.directionApi.getDirections('A', 'B');
}
}
Now you can easily mock placesApi and directionApi, and test them individually without actually requiring Google Map dependencies.
Hope this helps ! 😇
Related
I am unit testing a method defined in a module, which itself calls a dependency defined in a second module. I want to set a spy for the method-under-test's call to the dependency, using the Sinon package. How do I do that? I've seen Sinon's page on mocking the dependency of a module (see here), and have been able to use it successfully in the past. But in this case, my test code (see below) is still calling the original method, not the spy.
FYI in my code, if I assign a Sinon stub to the method, instead of Sinon spy, then the stubbed method is indeed called, as expected. I'm not sure why I can stub, but not spy in this case.
In this case, using stub is fine for my purposes. But I'm curious as to why I cannot use spy here as I've done in the past.
Thanks.
My Code
combo-test.js (test file)
const { tape } = require('tape')
const sinon = require('sinon')
const { myCombo } = require('./lib/ow/combo')
const { ComboDropdown } = require('../../../lib/ow/combo-dropdown')
const comboObject = myCombo(props)// Instantiate object to expose method-under-test.
sinon.spy(ComboDropdown.prototype, 'extMethod')// Mock call to external method with a spy.
// sinon.stub(ComboDropdown.prototype, 'extMethod')
comboObj.myMethod()// Prints to console: 555
combo.js (defines method-under-test)
const { ComboDropdown } = require('./combo-dropdown')
class Combo extends myClass {
constructor(props) {
}
myMethod() {// method-under-test
this.dropdown = new ComboDropdown({
})
this.dropdown.extMethod()//Calls external method.
}
}
const myCombo = props => new Combo(props)
module.exports = { myCombo }
combo-dropdown.js (defines external method)
class ComboDropdown extends Dropdown {
constructor(props) {
super(props)
}
extMethod() {
console.log(555)
}
}
module.exports = {
ComboDropdown
}
sinon.spy(object, "method") creates a spy that wraps the existing function object.method. The spy will behave exactly like the original method (including when used as a constructor), but you will have access to data about all calls.
sinon.spy() just add the calls information to the target method without changing its behavior or implementation, leaving it with its original implementation. With the calls information, you can make assertions after executing the code under test, such as whether the method was called or not.
If you want to have both the calls information and also change the implementation of the target method. sinon.stub(object, 'method') is the correct way. It will replace object.method with a stub function.
Besides, you can use such stub.returns(obj); API to make the stub return the provided value.
I want to test my React app and mock the backend calls. I decided to replace fetch by a Jest function that returns a constant.
The problem is that I can't overwrite the default fetch value. I googled a bunch and found global.fetch = ... to overwrite fetch but I'm not sure what global means. I tried just writing var fetch = ... in my test file but did not work although the component is within the scope of component.
I'm happy to hear alternative solutions for mocking fetch.
// Does not work
import component that fetches
test(...){
var fetch = ...
<component that fetches/>
}
// Works
import component that fetches
test(...){
global.fetch = ...
<component that fetches/>
}
It's expected that the first option doesn't work because fetch variable is local to a function where it was defined. although the component is within the scope of component statement doesn't make much sense, that a component is nested (or more specifically, React element, because <Comp/> translates to React.createElement(Comp)) doesn't mean it can access anything from that scope except variables that were specifically passed as props.
This works like:
function foo() {
var fetch = 'local';
var someGlobal = 'local';
console.log(fetch); // a global shadowed by a local
console.log(someGlobal); // a global shadowed by a local
bar(someGlobal);
}
function bar(someGlobal) {
console.log(fetch); // a global
console.log(someGlobal); // a global shadowed by a local
}
Since real requests aren't supposed to be performed in tests, it's acceptable to mock fetch by assigning it to a global like global.fetch = ..., but for other globals this would it impossible to restore original implementation. Generally, Jest spies should never be set by assignment. Instead, spyOn is used:
beforeEach(() => {
jest.spyOn(global, 'fetch').mockImplementation(...)
});
This allows the framework to restore original implementation if needed, this spy works correctly with both resetAllMocks and restoreAllMocks.
I need to mock API calls for a button click but the actual call is nested down in a utility file that is called by a middleware file. Some framework code was using Jest, axios-mock-adapter, and Enzyme. (I'm still wrapping my head around what each of these do).
So let me preface this. I'm an intern at a company where my task is to test some JS code for a piece of software built on a microservice architecture. So first let me apologize for any improper verbage. My background is in C/C++ and x86 assembly. No, I didn't fudge my resume when applying for this position. The company was fully aware that I had little to no experience with JS. I've attempted to create a mock = MockAdapter('axios') then calling that with mock.OnGet().reply() but when checking my coverage it seems to error every time.
Theres to much code to post so I'll try to give an example
class ComponentName extends component {
stuff
}
ComponentNameFunc {
this.middleware.funcName.then(
response ()=>{}
errorRespone ()={}
)
}
//funcName is a name of a middleware function that calls a function
//in the utility file. The utility file does the axios.get call
When I render the component then simulate a button click it calls this.middleware.funcName but then the coverage shows it going to the errorResponse portion. Heres a test example
describe('test',()=>{
test('button click', done => {
mock.onGet('aURL').reply(200,mockData);
Enzyme.configure({ adapter: new Adapter() });
const wrapper = shallow(
<ComponentName/>);
expect(wrapper.exists()).toBe(true);
wrapper
.find("Button")
.at(0)
.simulate("click");
done();
)};
)};
EDIT: So I found part of the issue. I had multiple mocks for different API calls and apparently only 1 was registering. However, some of these functions that I'm testing will make two API calls. How do I mock up two separate API calls for a single test? Originally I had some thing like this
import axios from "axios"
let mock = MockAdapter(axios);
let mock2 = MockAdapter(axios);
mock.OnGet("URL").reply(200,Data);
mock2.OnGet("URL2").reply(200,DifferentData);
So I figured it out. I was trying to make multiple mock variables (or are they objects?) like mock, mock2, mock3. It seems replicating mock.OnGet.reply with different information works just fine.
I have a method which uses an ElementRef which is defined below.
#ViewChild('idNaicsRef') idNaicsRef: ElementRef;
ElementRef then sets the focus using .nativeElement.focus().
The method fails while running the spec, saying 'undefined is an object'
Although httpNick's answer should work, I ended up asking an architect on my team about this and he led me to a slightly different solution that may be a bit simpler.
describe(MyComponent.name, () => {
let comp: MyComponent;
describe('myFunction', () => {
it('calls focus', () => {
comp.idNaicsRef = {
nativeElement: jasmine.createSpyObj('nativeElement', ['focus'])
}
comp.myFunction();
expect(comp.idNaicsRef.nativeElement.focus).toHaveBeenCalled();
});
});
This particular example would just test to see if the focus method has been called or not. That's the test that I was interested in when I was testing my method, but you could of course test whatever you wanted. The key is the setup beforehand (which was elusive before it was shown to me).
this should work. this just creates a spy object and then you can populate it with whatever you want, so you could even check if it was called in your unit test.
import createSpyObj = jasmine.createSpyObj;
comp.idNaicsRef = createSpyObj('idNaicsRef', ['nativeElement']);
comp.idNaicsRef.nativeElement = { focus: () => { }};
comp is the reference to the component you are testing.
createSpyObj comes from a jasmine import
I have the following test:
const mockedObject = {
mockedMethod: jest.fn((someKey, someValue) => {
return {someKey: 'someValue'}
})
};
jest.doMock('../myObject', () => {
return mockedObject;
});
testedObject.testedMethod();
expect(mockedObject.mockedMethod).toHaveBeenCalled();
Here, in the testedObject, I am importing myObject. I would like to mock that import and pass mockedObject instead.
After looking at this question and this question, I think the above code should be good, however mockedObject.mockedMethod is never called even though testedObject is making the call.
So what is wrong with the mocking done in this test?
You call
testedObject.testedMethod()
but expect
mockedObject.mockedMethod)
try this code:
const mockedObject = {
testedMethod: jest.fn((someKey, someValue) => {
return {someKey: 'someValue'}
})
};
jest.doMock('../myObject', () => {
return mockedObject;
});
testedObject.testedMethod();
expect(mockedObject.testedMethod).toHaveBeenCalled();
I can think of some choices.
One thing that could be happening is that you are mocking the import after your tested object has required it and you cannot modify that. If that is your case, then make sure you make the instance of the object only after you have modified the package.
Another option is to create a folder mocks and create a file .js called exactly like your module or import and then return whatever you need. This works best for global dependencies.
Lastly, what you also can do is to make your tested object receive the dependency as a parameter, so you can override the import inside the file.
Hope it helps