I have a file that exports a class. The file's structure is as follows:
// Import statements
// Component decorator
export class TheClass {
constructor() {
// some private service instances created using the imports
}
method1(): boolean {
// some logic, may return true or false, but is sure to eventually return true over successive calls
}
method2(): void {
// method2 is expected to retry if method1 returns false
if (!this.method1()){
setTimeout(this.method2, 100);
return;
}
let navbar = document.querySelector(...);
// a little more code
}
}
// statement to register a new directive
Its corresponding spec file (the-class.component.spec.ts), is structured as follows:
// Import statements
describe('TheClass', () => {
let fixture: ComponentFixture<TheClass>;
let component: TheClass;
// Some more identical statements
// A couple of beforeEach functions
// One single test:
it('should retry method2 if method1 returns false', fakeAsync(() => {
spyOn(document, 'querySelector').and.stub();
component.method1 = (): boolean => false;
component.method2();
tick(101);
fixture.whenStable().then(() => {
// Checks whether the function really did retry, or just went ahead normally, which it shouldn't
expect(document.querySelector).not.toHaveBeenCalled();
})
}
})
Note: both these pieces of code are minimum working examples. The actual 'the-class.component.ts' file has dozens of class properties and methods. The actual 'the-class.component.spec.ts' file has dozens of tests.
When the tests are run, this error is thrown:
Chrome Headless 95.0.4638.69 (Linux x86_64) TheClass should retry method2 if method1 returns false FAILED
TypeError: this.method1 is not a function
at truncateNavbar (core/templates/combined-tests.spec.js:6612:72265)
at ZoneDelegate../node_modules/zone.js/dist/zone.js.ZoneDelegate.invokeTask (core/templates/combined-tests.spec.js:666255:35)
at ProxyZoneSpec../node_modules/zone.js/dist/proxy.js.ProxyZoneSpec.onInvokeTask (core/templates/combined-tests.spec.js:665736:43)
at ZoneDelegate../node_modules/zone.js/dist/zone.js.ZoneDelegate.invokeTask (core/templates/combined-tests.spec.js:666254:40)
at Zone../node_modules/zone.js/dist/zone.js.Zone.runTask (core/templates/combined-tests.spec.js:666022:51)
at ./node_modules/zone.js/dist/zone.js.ZoneTask.invokeTask (core/templates/combined-tests.spec.js:666337:38)
at ZoneTask.invoke (core/templates/combined-tests.spec.js:666326:52)
at core/templates/combined-tests.spec.js:664645:24
at Scheduler../node_modules/zone.js/dist/fake-async-test.js.Scheduler.tick (core/templates/combined-tests.spec.js:664530:53)
at FakeAsyncTestZoneSpec../node_modules/zone.js/dist/fake-async-test.js.FakeAsyncTestZoneSpec.tick (core/templates/combined-tests.spec.js:664772:33)
Note: When method1 is called on its own in one of the other unit tests (not included in the mwe), it doesn't cause any error whatsoever. However, when method2 is called, which then proceeds to call method1, the error arises.
What am I doing wrong here?
A little context:
This is part of an open-source project. I was working on fixing an issue for the project. The state of the code before I picked it up was this:
The component.method1 = (): boolean => false; assignment was originally component.method1 = null.
And in the method2 definition, the if statement was originally as follows:
if (!this.method1) {
setTimeout(this.method2, 100);
return;
}
Notice the lack of parentheses in the condition. method1 isn't actually called.
Part of the issue I was working on fixing was ensuring correct types and assignments. So assigning null to a method was a no-no. The most straightforward solution was
- changing the
nullassignment to assigning a function that returns false - adding a pair of parentheses (in the if condition) to actually call the method
The code and the tests worked fine in the original state. After these two changes, the 'not a function' error popped up.
P.S. I'm new to Angular, and typescript.