I'm having a rough time trying to test if some function is bound to a component after the component has been initialized.
This is my ngOnInit() function:
ngOnInit() {
this.someFunction = this.someFunction.bind(this);
}
And this is the function that I want to bind to the component::
someFunction() {
// this empty function is not called yet but should be bound to the component
}
And this is my before each:
beforeEach(async(() => {
fixture = TestBed.createComponent(ComponentName);
component = fixture.componentInstance;
fixture.detectChanges();
}));
And this is my describe function:
describe('ngOnInit', () => {
it('someFunction has been bound to the component.', () => {
let bindFunctionSpy = spyOn(component.someFunction, 'bind').and.callThrough();
component.ngOnInit();
expect(bindFunctionSpy).toHaveBeenCalledWith(component);
});
});
The problem that I'm facing here is that there is a typescript error in the spyOn function preventing me from compiling the test cases, it says :
error TS2345: Argument of type '"bind"' is not assignable to parameter of type 'never'.
So what exactly am I not doing right here?
The same thing happens if I try spying on any of the prototype functions for a component function like apply or call for example.
Yet if I tried to spy on a prototype function for a component variable like length or toLowerCase it doesn't throw such error!
Another note is that this test actually sometimes gets compiled successfully and actually passes, and sometimes it throws the error while compilingm, but it happens only when I make any random changes like adding a space then saving them so that Karma can detect that a change happened and recompile the tests, but if I closed the terminal and then started it again and ran ng test I get the error again.
The best way to do this is just casting your function to the CallableFunction type.
let bindFunctionSpy = spyOn(component.someFunction as CallableFunction, 'bind').and.callThrough();
You could try
let bindFunctionSpy = spyOn(component.someFunction.prototype, 'bind').and.callThrough();
Try spying on Function.prototype like this -
spyOn(Function.prototype, 'bind');
it worked for me.
Related
I want to set a unit test on a very simple Vue Component with the default Vue Test Utils plugin coupled to Jest Framework.
On a click of a button, the handler calls 2 methods:
emitEvent(): to emit an event (actual target of my test),
effectUI(): for UI effect (using the Web Animations API). This animation is applied on each 'particle' of a 'particles' array. I do not wish to test this part (yet), but this is the one which is problematic.
I works fine when I run the component. No warnings, no errors.
But when I run the test, it passes... with console.error stating that 'particle.animate' is not a function.
I have tried:
first, to do nothing special: since the EffectUI() method has nothing to do with the click event (except they are called by the same handler) so maybe they do...
then, to mock the "animate" function: with no result so far. I assume the issue comes from the Web API method not being recognized. I may be completely wrong.
Code of method called from component click's handler:
effectUI() {
let particles = this.$el.querySelectorAll('span.particle')
particles.forEach(particle => { particle.animate(...) }
}
Code of test file:
import { mount } from '#vue/test-utils'
import ButtonParticles from '#/components/ButtonParticles.vue'
describe('ButtonParticles.vue', () => {
const wrapper = mount(ButtonParticles)
const animStub = jest.fn()
it('should trigger `clicked` event when user clicks on button', () => {
let particles = wrapper.findAll('.particle')
particles.wrappers.forEach(particle => {
particle.animate = animStub
})
wrapper.find('button').trigger('click')
expect(wrapper.emitted().clicked).toBeTruthy()
})
})
Expected results would be to get no console.error
Actual results are: [Vue warn]: Error in v-on handler: "TypeError: particle.animate is not a function" (+ stack trace)
Anyone can help me understand what's happening?
Thanks!
In your test, particle is a wrapper. Try particle.element.animate = animStub
I have a React component 'A'. One method 'foo' inside it is passed as a prop to component 'B'. foo is triggered on click of component B.
Question - How to test this foo method?
I can circumvent this problem by making the method foo as public and testing it separately. But I do not want to make it public.
Another way which I tried and did not work is triggering the click event in component B hoping it would call the foo method. Not sure if it is possible and if possible how!
const A = () => {
const foo = () => {console.log('Clicked!')}
return (
<B clickTrigger={foo} />
)
}
It sounds like you want to test that the click is causing some change in the component, rather than just checking that the method is called.
You can render component A, fire a click event, and make assertions based on how that causes changes in the component's output. Here's what a test could look like, using react-testing-library:
test('does a thing when clicked', () => {
const { getByText } = render(<A />);
// This assumes you have a button inside component B
// that has `foo` as the onClick:
fireEvent.click(getByText('Click me'));
// Make assertions here based on how the click handler
// causes the component's output to change, generally based
// on some text changing
expect(getByText('You clicked the button')).toBeInTheDocument();
})
You should mock the code executed inside the foo function.
Thanks to this mock, you will be able to test of your foo function has been successfully called .
There's no way to get hold of a reference inside a closure, so you'd have to export it to your test framework somehow. There's a pattern that uses WeakMaps for storing private state, and if the test framework has access to the WeakMap it can look inside, while other objects without that reference can't.
let p = new WeakMap();
const A = () => {
let foo = p.get(A);
return (
<B clickTrigger={foo} />
);
};
p.set(A, () => {console.log('Clicked!')});
// Export A and p to testing framework but just A to clients
i have noticed that when you use a custom component with a input property and you bind a function to it this function is called lots of times.
E.g.
<some-tag [random-input]="randomFunction()"></some-tag>
and in the class
private randomFunction() {
console.log('Called!');
return true
}
if you run something simple as this you will see in the console a few dozens of 'Called!' logs.
In my project the randomFunction makes a call to the database, so this is pretty anoying.
Does anyone knows why is this happening?
Angular runs this with every cycle, trying to check for updated value, that's why you see so many messages in the log.
For this reason it is not good practice to have ts functions as inputs to component.
You can for example make a call to the server/database in constructor, OnInit or OnChanges, store the result to local variable and make that variable as input to component. Something similar to this:
export class MyComp {
dbResult: any;
constructor(http: HttpClient) {
http.get('/my/api/call').subscribe(result => {
this.dbResult = result;
});
}
....
}
..and in HTML:
<some-tag [random-input]="dbResult"></some-tag>
As a sidenote, having that function marked as private will eventually fail during ng build --prod
Angular needs to check if the value has changes otherwise it can't update the value inside the component.
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 ! 😇
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