Issue
I am new to Angular and am trying to write unit tests for a feature I recently implemented. The feature works fine but the unit tests won't even compile due to the following error:
NullInjectorError: R3InjectorError(DynamicTestModule)[BillingPageComponent -> BillingPageComponent]:
NullInjectorError: No provider for BillingPageComponent!
The stack trace looks like this:
at NullInjector.get (../../node_modules/@angular/core/fesm2022/core.mjs:8890:27)
at R3Injector.get (../../node_modules/@angular/core/fesm2022/core.mjs:9334:33)
at R3Injector.get (../../node_modules/@angular/core/fesm2022/core.mjs:9334:33)
at ChainedInjector.get (../../node_modules/@angular/core/fesm2022/core.mjs:14018:36)
at lookupTokenUsingModuleInjector (../../node_modules/@angular/core/fesm2022/core.mjs:4608:39)
at getOrCreateInjectable (../../node_modules/@angular/core/fesm2022/core.mjs:4656:12)
at ɵɵdirectiveInject (../../node_modules/@angular/core/fesm2022/core.mjs:11801:19)
at NodeInjectorFactory.BillingPaymentComponent_Factory [as factory] (..\..\ng:\BillingPaymentComponent\ɵfac.js:6:58)
at getNodeInjectable (../../node_modules/@angular/core/fesm2022/core.mjs:4862:44)
at createRootComponent (../../node_modules/@angular/core/fesm2022/core.mjs:14273:35)
at ComponentFactory.create (../../node_modules/@angular/core/fesm2022/core.mjs:14137:25)
at initComponent (../../node_modules/@angular/core/fesm2022/testing.mjs:27492:51)
at _ZoneDelegate.Object.<anonymous>._ZoneDelegate.invoke (../../node_modules/zone.js/bundles/zone.umd.js:411:30)
at ProxyZoneSpec.Object.<anonymous>.ProxyZoneSpec.onInvoke (../../node_modules/zone.js/bundles/zone-testing.umd.js:300:43)
at _ZoneDelegate.Object.<anonymous>._ZoneDelegate.invoke (../../node_modules/zone.js/bundles/zone.umd.js:410:56)
at Object.onInvoke (../../node_modules/@angular/core/fesm2022/core.mjs:11083:33)
at _ZoneDelegate.Object.<anonymous>._ZoneDelegate.invoke (../../node_modules/zone.js/bundles/zone.umd.js:410:56)
at Zone.Object.<anonymous>.Zone.run (../../node_modules/zone.js/bundles/zone.umd.js:165:47)
at NgZone.run (../../node_modules/@angular/core/fesm2022/core.mjs:10934:28)
at _TestBedImpl.createComponent (../../node_modules/@angular/core/fesm2022/testing.mjs:27495:41)
at Function.createComponent (../../node_modules/@angular/core/fesm2022/testing.mjs:27302:37)
at src/app/pages/billing-page/payment/payment.component.spec.ts:118:23 [<-- This is my spec file]
at _ZoneDelegate.Object.<anonymous>._ZoneDelegate.invoke (../../node_modules/zone.js/bundles/zone.umd.js:411:30)
at ProxyZoneSpec.Object.<anonymous>.ProxyZoneSpec.onInvoke (../../node_modules/zone.js/bundles/zone-testing.umd.js:300:43)
at _ZoneDelegate.Object.<anonymous>._ZoneDelegate.invoke (../../node_modules/zone.js/bundles/zone.umd.js:410:56)
at Zone.Object.<anonymous>.Zone.run (../../node_modules/zone.js/bundles/zone.umd.js:165:47)
at Object.wrappedFunc (../../node_modules/zone.js/bundles/zone-testing.umd.js:789:34)
Based on the error, it would seem that this would be a TestBed.configureTestingModule
issue. However, I do, in fact, provide a mocked version of the component (specifically, a 'SpyObj' of component BillingPaymentComponent
) as shown here in my beforeEach()
method:
beforeEach(() => {
spyBillingPageComponent = createSpyObj<BillingPageComponent>('BillingPageComponent', ['goBillingList', 'payNow']);
spyBillingService = createSpyObj<BillingService>('BillingService', [
'clearSelected',
'getPaymentLines',
'getPaymentTableCells',
'setComponent',
'totalAmount',
]);
spyPaymentClient = createSpyObj<PaymentClient>('PaymentClient', ['bank']);
spyUserContextService = createSpyObj<UserContextService>('UserContextService', ['get']);
TestBed.configureTestingModule({
declarations: [BillingPaymentComponent],
imports: [DXONgSwiftUiModule, FormsModule, ReactiveFormsModule],
providers: [
{ provide: BillingPageComponent, useValue: spyBillingPageComponent },
{ provide: BillingService, useValue: spyBillingService },
{ provide: PaymentClient, useValue: spyPaymentClient },
{ provide: UserContextService, useValue: spyUserContextService },
],
}).compileComponents();
fixture = TestBed.createComponent(BillingPaymentComponent);
...
By the way, the error shown in the stack trace occurs at the last line shown (fixture = ...
).
If it helps, here are the import statements:
import { AccountType } from '@benefitsui/app/api/benefits-enums';
import { PaymentData } from '@benefitsui/app/pages/billing-page/billing-page.types';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { BillingPaymentComponent } from '@benefitsui/app/pages/billing-page/payment/payment.component';
import { BillingService } from '@benefitsui/app/pages/billing-page/billing.service';
import { DXONgSwiftUiModule } from '@dxo/ng-swift-ui';
import { SpyObj, createSpyObj } from '@benefits/test-utils';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { BillingPageComponent } from '../billing-page.component';
import { PaymentClient } from '@benefitsui/app/api/benefits-clients';
import { UserContextService } from '@benefitsui/app/services/user-context/user-context.service';
import { UserContext } from '@benefitsui/app/services/user-context/user-context.types';
import { of } from 'rxjs';
Here is the constructor for the component under test, which shows the dependency injection that occurs:
constructor(
private billingService: BillingService,
private formBuilder: UntypedFormBuilder,
private parent: BillingPageComponent,
private paymentClient: PaymentClient,
private userContextService: UserContextService
) {}
I am on a Windows laptop. My version of Node is v18.18.2.
Given that the feature itself works fine, I would at least like the unit tests to compile so that I can debug them and provide protection against regressions.
Any ideas on how to solve the problem?
Solution
After struggling with this issue for several days, I found that the issue appears to be a Red Herring. The solution turns out to be a pathing issue with the imports. Specifically, I shifted the relative pathing to absolute pathing, which fixed the entire problem. Here is the specific fix:
...
// import { BillingPageComponent } from '../billing-page.component';
import { BillingPageComponent } from '@benefitsui/app/pages/billing-page/billing-page.component';
...
This one fix solves the entire compilation problem.
The real issue here is why this is necessary. It appears related to the fact that I am developing on a Windows-based machine. A clue to this solution came from a colleague who downloaded my branch and did NOT have the compilation issue. (He develops on a Linux-based machine.)
I don't know if this is an issue with the mocking library built into Angular or if it is an issue inherent with Microsoft pathing. Either way, though, this turned out to be a very frustrating and time-consuming bug that had nothing to do with the reported error so I wanted to share my solution to save other developers the same heartache I experienced.
Answered By - Bruce Hall
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.