1

I'm working on a small test app to learn Angular 2 and unit testing in Angular 2 a bit better. Coming from a react - Jest background; it feels weird to include all components in the app.component.ts.

This is what the component Hierarchy looks like for what I have so far:

App.Component > Layout.Component > Header.Component > Nav.Component

When I run npm test, I get the error of

main-header' is not a known element:

When I'd fix this by Importing and declaring the component bound to that selector in App.Component.spec.ts, I would just get the same error for the next component/selector inline.

Had this been a large app with many components; I could see App.Component's Test becoming large and unmaintainable. After several google searches that yielded AngularJS and Angular RC.X results.. I've hit a dead end. My Gut is telling me something is wrong and that this can't be the way Testing in Angular 2 is done.. but I could be wrong!

calcazar
  • 1,120
  • 1
  • 12
  • 22
  • Possible duplicate of [Angular 2 Karma Test 'component-name' is not a known element](https://stackoverflow.com/questions/44504468/angular-2-karma-test-component-name-is-not-a-known-element) – Kim Kern Aug 02 '18 at 01:09

2 Answers2

2

Please give more details about the way you generate your project on Angular. I recommend you to use Angular CLI to generate your project/components/pipes/services because it will generate a spec.ts file for each on your behalf and will also link your components to a module.

Then, think about the test class of Angular as an empty test bench where you have to recreate the structure of your component in order to be able to test. In the actual app, all binding is made inside of the module, but this is not the case of tests where you have to import and declare all the components of a component in order for test framework to be able to build it and test it.

In the structure you presented, you are going to have a test class for each component like:

Nav.Component.spec.ts
Header.Component.spec.ts
Layout.Component.spec.ts
App.Component.spec.ts

And given the hierarchy, on each you will do something like:

Nav.Component.spec.ts

 import { NavComponent } from './your path to component';
 ...
 beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [ NavComponent  ]
    })
    .compileComponents();
 }));

Header.Component.spec.ts

 import { NavComponent } from './your path to component';
 import { HeaderComponent } from './your path to component';
 ...
 beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [ NavComponent , HeaderComponent ]
    })
    .compileComponents();
 }));

Layout.Component.spec.ts

 import { NavComponent } from './your path to component';
 import { HeaderComponent } from './your path to component';
 import { LayoutComponent } from './your path to component';
 ...
 beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [ NavComponent , HeaderComponent , LayoutComponent ]
    })
    .compileComponents();
 }));

App.Component.spec.ts

 import { NavComponent } from './your path to component';
 import { HeaderComponent } from './your path to component';
 import { LayoutComponent } from './your path to component';
 import { AppComponent } from './your path to component';
 ...
 beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [ NavComponent , HeaderComponent , LayoutComponent, AppComponent  ]
    })
    .compileComponents();
 }));

If you do it like this, should work. The whole idea is to reconstruct on test each element with all the elements it uses in order to work. If something is not clear enough or not working, put more details.

The example I give with importing each component is the way to make it work with your presented structure of components not linked on modules. But, as I told you, this is not the way it was intended to be built. If you generate with Angular CLI you can do the following: Generate your module like:

ng generate module layout -m app

This will generate your layout module and import it for you into app.module Generate then all your components the same way:

ng generate component layout -m layout
ng generate component header -m layout
ng generate component nav -m layout

This generates all you components, import each into the layout.module, which is already imported into app.module. This way you don't have to import anything more and your tests will just work.

This is how each element will look like after:

app.module

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { AppComponent } from './app.component';
import { LayoutModule } from './layout/layout.module';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    LayoutModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

layout module

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { LayoutComponent } from './layout.component';
import { HeaderComponent } from '../header/header.component';
import { NavComponent } from '../nav/nav.component';

@NgModule({
  imports: [
    CommonModule
  ],
  declarations: [LayoutComponent, HeaderComponent, NavComponent]
})
export class LayoutModule { }

layout component

import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-layout',
  templateUrl: './layout.component.html',
  styleUrls: ['./layout.component.css']
})
export class LayoutComponent implements OnInit {

  constructor() { }

  ngOnInit() {
  }

}

header component

import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-header',
  templateUrl: './header.component.html',
  styleUrls: ['./header.component.css']
})
export class HeaderComponent implements OnInit {

  constructor() { }

  ngOnInit() {
  }

}

nav component

import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-nav',
  templateUrl: './nav.component.html',
  styleUrls: ['./nav.component.css']
})
export class NavComponent implements OnInit {

  constructor() { }

  ngOnInit() {
  }

}
BogdanC
  • 2,746
  • 2
  • 24
  • 22
  • I think you've answered the question. I am currently using Angular-cli to generate my components. Looking at your App.Component.Spec.ts my gut feeling was wrong and what I was doing was correct. So if you create an application with a total say, 80 components, you are expected to include all of those components in to the App.Component.Spec.ts file correct? – calcazar Jun 28 '17 at 20:49
  • You can do that, but is not the way to build your Angular app. For modularity Angular provide the NgModule class to help us keep the components/services/pipes related in blocks that after we use to build our app. In your case, let's say you declare a LayerModule where you import LayoutComponent, HeaderComponent and NavComponent. Then you will only import the LayerModule into your AppModule instead of the three components, same goes for the spec.ts class also. – BogdanC Jun 28 '17 at 23:28
  • This way, your app with 80 components, they will be grouped into modules imported into oder modules, so at the end you end up with only a few modules to import into your app.module. – BogdanC Jun 28 '17 at 23:28
  • But that's not how it works according to what I've seen and your example above. When you import LayerModule app.spec will ask you to import the three components within. I probably misunderstood, can you provide an example of the LayerModule? – calcazar Jun 29 '17 at 00:17
0

If you have a few components grouped in one module you can import module to test instead of declaring each component.

beforeEach(async(() => {
  TestBed.configureTestingModule({
    declarations: [AppComponent],
    imports: [SomeModule]
  }).compileComponents();
}));