Reputation: 8661
I have a helper file with multiple function:
myHelpers.ts
export function fn1(a, b) {
return a * b;
}
export function fn2(a, b) {
return a + b;
}
export function fn3(a, b) {
return a - b;
}
The above file, as well are others are bundled as a module and exported in the index.ts as
import * as Helpers from '/myHelpers';
import * as DaoUtils from '/daoUtils';
export { Helpers };
export { DaoUtils };
I consume the above module in my app package.json as:
"helper-utils": "file:/local/app/helper"
Which my angular component uses:
import { Helpers } from 'helper-lib';
export class MyComponent {
constructor() {}
public btnClick(p1, p2) {
let value = Helpers.fn1(p1, p2);
console.log(value);
return value;
}
}
The above works fine and if on btnClick() in the GUI i pass 1 and 2, i see the value of 2 in the console.log.
Unit test:
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
import { MyComponent } from './myComponent';
import { Helpers } from 'helper-lib';
describe('MyComponent', () => {
let component: MyComponent;
let fixture: ComponentFixture<MyComponent>;
beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({declarations:[MyComponent]}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(MyComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should calc value', () => {
let mySpy = spyOn(Helpers, 'fn1');
let value = component.btnClick(1, 2);
expect(mySpy).toHaveBeenCalled();
});
});
Error:
Error: <spyOn> : fn1 is not declared writable or has no setter
Upvotes: 1
Views: 1584
Reputation: 1264
If your helpers are pure functions (always return the same result if the same arguments are passed in) then it is perfectly okay not to spy on them and instead expect what the final value should be. This does very heavily tie your tests to the helper, but normally there is only one way to do certain calculations and the helper would have its own spec to prove any complicated logic, where your component would likely only use simpler values.
it('should calc value', () => {
let value = component.btnClick(1, 2);
expect(value).toBe(2);
});
If that isn't the case one other option is to create a new helpers file that looks roughly like
export class Helpers {
static fn1(a: number, b: number) {
return a * b;
}
static fn2(a: number, b: number) {
return a + b;
}
static fn3(a: number, b: number) {
return a - b;
}
}
Now your test can spy on it all it wants
it('should calc value', () => {
const mySpy = spyOn(Helpers, 'fn1').and.callThrough();
const value = component.btnClick(1, 2);
expect(mySpy).toHaveBeenCalled();
expect(value).toBe(2);
});
Upvotes: -1
Reputation: 18849
Unfortunately, I am thinking you're coming across this issue: https://stackoverflow.com/a/62935131/7365461.
As TypeScript advanced, most likely the answers linked will not work anymore.
The answer linked and the Github thread linked in the answer explains this.
An unfortunate reality with Angular is that to make testing easier, you should always be thinking about dependency injection (in essence, write re-usable code that can be used everywhere in a service). This way, it becomes easier to test.
The answer I have linked creates a class with a static method that calls the functions and then you can spy on these static methods as well. This way works as well.
Upvotes: 2