163

In the AppComponent, I'm using the nav component in the HTML code. The UI looks fine. No errors when doing ng serve. and no errors in console when I look at the app.

But when I ran Karma for my project, there is an error:

Failed: Template parse errors: 
'app-nav' is not a known element:
1. If 'app-nav' is an Angular component, then verify that it is part of this module.
2. If 'app-nav' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' to the '@NgModule.schemas' of this component to suppress this message.

In my app.module.ts:

there is:

import { NavComponent } from './nav/nav.component';

It is also in the declarations part of NgModule

@NgModule({
  declarations: [
    AppComponent,
    CafeComponent,
    ModalComponent,
    NavComponent,
    NewsFeedComponent
  ],
  imports: [
    BrowserModule,
    FormsModule,
    HttpModule,
    JsonpModule,
    ModalModule.forRoot(),
    ModalModule,
    NgbModule.forRoot(),
    BootstrapModalModule,
    AppRoutingModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})

I'm using the NavComponent in my AppComponent

app.component.ts

import { Component, ViewContainerRef } from '@angular/core';
import { Overlay } from 'angular2-modal';
import { Modal } from 'angular2-modal/plugins/bootstrap';
import { NavComponent } from './nav/nav.component';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  title = 'Angela';
}

app.component.html

<app-nav></app-nav>
<div class="container-fluid">
</div>

I have seen a similar question, but the answer in that question says we should add NgModule in the nav component that has a export in that, but I'm getting compile error when I do that.

There is also: app.component.spec.ts

import {NavComponent} from './nav/nav.component';
import { TestBed, async } from '@angular/core/testing';
import { AppComponent } from './app.component';
Kim Kern
  • 54,283
  • 17
  • 197
  • 195
Angela P
  • 1,939
  • 2
  • 14
  • 18
  • You're likely missing an import in your spec file. I'm assuming the spec test is on app.spec.ts, so you'll want to `import { NavComponent }` in your spec.ts – Z. Bagley Jun 12 '17 at 17:20
  • 1
    it's imported. I was missing the declaration part – Angela P Jul 24 '17 at 20:17
  • 1
    Importing and declaring the custom component inside app.component.spec.ts worked for me, thanks guys! – ENDEESA Jan 04 '19 at 11:18

7 Answers7

241

Because in unit tests you want to test the component mostly isolated from other parts of your application, Angular won't add your module's dependencies like components, services, etc. by default. So you need to do that manually in your tests. Basically, you have two options here:

A) Declare the original NavComponent in the test

describe('AppComponent', () => {
  beforeEach(async(() => {
      TestBed.configureTestingModule({
        declarations: [
          AppComponent,
          NavComponent
        ]
      }).compileComponents();
    }));

B) Mock the NavComponent

describe('AppComponent', () => {
  beforeEach(async(() => {
      TestBed.configureTestingModule({
        declarations: [
          AppComponent,
          MockNavComponent
        ]
      }).compileComponents();
    }));

// it(...) test cases 

});

@Component({
  selector: 'app-nav',
  template: ''
})
class MockNavComponent {
}

You'll find more information in the official documentation.

Kim Kern
  • 54,283
  • 17
  • 197
  • 195
  • 2
    Thanks for this. I've run into the issue of having to import multiple components and modules to the point where it makes far more sense to just import the `AppModule` in the TestBed configuration. Would you recommend against this? – mcheah Dec 31 '18 at 06:14
  • 3
    @jonathan maybe the component you declared has dependencies of its own? In a unit test, it's better to use mocks. – Kim Kern Sep 01 '19 at 07:08
  • 1
    Is there any way to configure the logging to show test names or any relevant info that shows which spec is causing these messages? – claudekennilol Feb 26 '21 at 19:13
  • Documentation has moved to https://angular.io/guide/testing-components-scenarios#nested-component-tests. – Big McLargeHuge Jun 13 '21 at 04:53
  • 1
    For those still having problems - make sure to do it in ALL test files. I spent whole day trying to figure out why I still get this error, turned out I used the parent component elsewhere in the tests. – user1328889 Mar 29 '22 at 09:46
  • Love this approach - particularly option B – Gregor Jul 04 '23 at 18:14
12

You can also use NO_ERRORS_SCHEMA

describe('AppComponent', () => {
beforeEach(async(() => {
  TestBed.configureTestingModule({
    declarations: [
      AppComponent
    ],
    schemas: [NO_ERRORS_SCHEMA]
  }).compileComponents();
}));

https://2018.ng-conf.org/mocking-dependencies-angular/

Katherine R
  • 1,107
  • 10
  • 20
Adrian
  • 406
  • 4
  • 9
  • 3
    are there any potential issues that will arise out of this? It seems like a convenient fix but are there any important errors that will be quashed by this? – mcheah Jun 01 '18 at 19:43
  • 13
    This is what the [testing docs](https://angular.io/guide/testing#no_errors_schema) say: "The NO_ERRORS_SCHEMA also prevents the compiler from telling you about the missing components and attributes that you omitted inadvertently or misspelled. You could waste hours chasing phantom bugs that the compiler would have caught in an instant." – Kim Kern Aug 06 '18 at 00:00
  • 9
    you definitely don't wont to introduce extra implicit behavior into your unit tests: using NO_ERRORS_SCHEMA will encourage you to put dependencies into the 'grey' zone of between 'mocked' and 'pulled in'. any changes to those dependencies can potentially trigger breaking of seemingly unrelated unit tests -- no good – averasko Mar 20 '19 at 15:30
3

For me importing the component in the parent resolved the issue.

describe('AppComponent', () => {
  beforeEach(async(() => {
      TestBed.configureTestingModule({
        declarations: [
          AppComponent,
          NavComponent
        ]
      }).compileComponents();
    }));

Add this in spec of the parent where this component is used.

Kailas
  • 7,350
  • 3
  • 47
  • 63
1

Another source for this error are the tests for the parent component, app-root, that include the tag of this component, app-nav.

Even though the message is about app-nav, the child component, the fix should be added in the tests of app-root, the parent component.

The fix can be a mock:

app.component.spec.ts:

@Component({selector: 'app-nav', template: ''})
    class NavComponentStub {
}

What happens is that you create the root component, with it's tests, they pass. Latter on you add the child component in the root component tag and now you have to update the root component tests even if you just added a tag in the template.

Also the message doesn't say which test fails and from the message you might be led to believe that it's the child component's tests, when in fact they are the parent's tests.

GoTo
  • 5,966
  • 2
  • 28
  • 31
0

One more reason is that there can be multiple .compileComponents() for beforeEach() in your test case

for e.g.

beforeEach(async(() => {
  TestBed.configureTestingModule({
    declarations: [TestComponent]
  }).compileComponents();
}));

beforeEach(() => {
  TestBed.configureTestingModule({
    imports: [HttpClientModule],
    declarations: [Test1Component],
    providers: [HttpErrorHandlerService]
  }).compileComponents();
});
Spikolynn
  • 4,067
  • 2
  • 37
  • 44
Devaarth
  • 241
  • 2
  • 7
0

Step 1: Create stubs at beginning of spec file.

@Component({selector: 'app-nav', template: ''})
class NavComponent{}

Step 2: Add stubs in component's declarations.

TestBed.configureTestingModule({
  imports: [
    RouterTestingModule
  ],
  declarations: [
    AppComponent,
    NavComponent
  ],
}).compileComponents();
Yuvraj Patil
  • 7,944
  • 5
  • 56
  • 56
0

If you create a stub and still get the same error it might be because of --watch mode on. Try to stop it and run again.

Experimenter
  • 2,084
  • 1
  • 19
  • 26