Home > Software design >  angular unit test - no provider for parent component
angular unit test - no provider for parent component

Time:12-13

I'm creating my own angular bootstrap library, and at the moment I'm stuck writing my unit tests for the navbar.

I've distilled the troubled unit test down to the bare essentials and below is the summary:

Unit test

describe('BsNavbarDropdownComponent', () => {
  let component: BsNavbarDropdownTestComponent;
  let fixture: ComponentFixture<BsNavbarDropdownTestComponent>;

  beforeEach(async () => {
    await TestBed.configureTestingModule({
      imports: [
        RouterTestingModule.withRoutes([])
      ],
      declarations: [
        // Component to test
        BsNavbarDropdownComponent,

        // Mock components
        BsNavbarItemMockComponent,

        // Testbench
        BsNavbarDropdownTestComponent
      ],
      providers: [
        { provide: BsNavbarItemComponent, useClass: BsNavbarItemMockComponent }
      ]
    })
    .compileComponents();
  });

  beforeEach(() => {
    fixture = TestBed.createComponent(BsNavbarDropdownTestComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('should create', () => {
    expect(component).toBeTruthy();
  });
});

@Component({
  selector: 'bs-navbar-test',
  template: `
    <bs-navbar-item>
      <a [routerLink]='[]'>dropdown</a>
      <bs-navbar-dropdown>
        <bs-navbar-item>
          <a [routerLink]='["/b", "c"]'>bc</a>
        </bs-navbar-item>
      </bs-navbar-dropdown>
    </bs-navbar-item>`
})
class BsNavbarDropdownTestComponent {
}

@Component({
  selector: 'bs-navbar-item',
  template: `
  <li>
    <ng-content></ng-content>
  </li>`
})
class BsNavbarItemMockComponent {
}

Unit-to-test

@Component({
  selector: 'bs-navbar-dropdown',
  templateUrl: './navbar-dropdown.component.html',
  styleUrls: ['./navbar-dropdown.component.scss']
})
export class BsNavbarDropdownComponent {

  constructor(
    @Host() @Inject(forwardRef(() => BsNavbarItemComponent)) navbarItem: BsNavbarItemComponent
  ) {
    this.navbarItem = navbarItem;
  }

  navbarItem: BsNavbarItemComponent;

}

In the BsNavbarDropdownTestComponent it's blindly obvious that the bs-navbar-dropdown (BsNavbarDropdownComponent) has a parent tag bs-navbar-item (BsNavbarItemMockComponent). In the unit-to-test I'm injecting the BsNavbarItemComponent, which is in the testing module provided as BsNavbarItemMockComponent.

However it seems like the unit test is still not succeeding.

npm run nx run-many -- --target=test --projects=ng-bootstrap-demo --with-deps  --watch=false --browsers=ChromeHeadless

Result:

 FAIL   xxx-ng-bootstrap  libs/xxx-ng-bootstrap/src/lib/components/navbar/navbar-dropdown/navbar-dropdown.component.spec.ts
  ● BsNavbarDropdownComponent › should create

    NG0201: No provider for BsNavbarItemComponent found in NodeInjector. Find more at https://angular.io/errors/NG0201

Again, note that the module has the following providers:

providers: [
  { provide: BsNavbarItemComponent, useClass: BsNavbarItemMockComponent }
]

So what's wrong with this? Why is the BsNavbarItemComponent provider not found in the unit test?

CodePudding user response:

module provider is not being injected because of @Host annotation on the injection.

so your mock component should be injectable by the original component token

@Component({
  selector: 'bs-navbar-item',
  template: `
  <li>
    <ng-content></ng-content>
  </li>`,
  providers: [provide: BsNavbarItemComponent, useExisting: forwardRef(() => BsNavbarItemMockComponent)]
})
class BsNavbarItemMockComponent {
}
  • Related