Unit Testing angular2 component with imported module
Asked Answered
F

3

17

I am trying to write a test on a component which uses angular-material2, but when I add it to my testModule declarations I get:

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

Adding MaterialModule to the declarations throws `Error: Unexpected module 'MaterialModule' declared by the module

DynamicTestModule' in config/spec-bundle.js (line 24994)

This is what my spec file looks like:

  beforeEach(() => TestBed.configureTestingModule({
    declarations: [],
    providers: [
      { provide: DataService, useValue: mockDataService },
      { provide: ActivatedRoute, useClass: MockActivatedRoute },
      { provide: Router, useValue: mockRouter },
      CellViewComponent
    ]
  }));

adding CellViewComponent to the declarations array causes the error to throw.

Farcy answered 26/10, 2016 at 17:46 Comment(0)
H
27

When you use the TestBed.configureTestingModule, you're create a module from scratch for the test environment. So what ever you would need in the real application for the CellViewComponent to work, you also need to configure it in the testing module.

In your case, you're missing the Material card component. In the app you probably either imported the MaterialModule or the MdCardModule into your AppModule. So you need to do the same in the testing module

beforeEach(() => TestBed.configureTestingModule({
  imports: [ MaterialModule /* or MdCardModule */ ],
  declarations: [  CellViewComponent ],
  providers: [
    { provide: DataService, useValue: mockDataService },
    { provide: ActivatedRoute, useClass: MockActivatedRoute },
    { provide: Router, useValue: mockRouter },
  ]
}));
Hoopoe answered 26/10, 2016 at 17:55 Comment(2)
If I add Material Module to an imports array, I just get Disconnected, because no message in 10000 ms. - no tests are run...Farcy
You can either run the test in watch mode, or check this outHoopoe
D
12

This is a real problem: you can mock everything but the imported component's selector.

There is an easy way. It allows to avoid importing the modules, instead you can just disable this kind of errors.

Just add this to your module:

import { NO_ERRORS_SCHEMA } from '@angular/core';

...

TestBed.configureTestingModule({
  schemas: [ NO_ERRORS_SCHEMA ],
  ...

Angular2 docs link

Yes, it won't help if you want to make integration (not isolated) tests, but it perfectly works for isolated ones.

Still even if you would decide to import a module I think it might be more correct to import the mock module with all implemented selectors instead.

Douro answered 8/11, 2016 at 11:19 Comment(0)
G
1

What I often do, when testing our Angular application components, is just import the parent module via reference. For most use cases, it is enough or close to enough and if you change the component, via adding new declarations or imports, then you do not need to worry about changing the test file, because the test file imports the parent module.

I only ever change the module to import some external components for testing purposes, but this is rare.

Regular test initialization pseudo code

beforeEach(() => TestBed.configureTestingModule({
    declarations: [
        ComponentA,
        ComponentB
    ],
    providers: [
        CellViewComponent
    ]
}));

let's say that this component is in a module. I put the declarations object into a variable for use in ParentModule and Testing simultaneously.

export var ParentModuleArgs = {
    declarations: [
        ComponentA,
        ComponentB
    ],
    providers: [
        CellViewComponent
    ]
  };

@NgModule(parentModuleArgs)
export class ParentModule {}

Then, instead of rewriting the whole Module array into the testing component and being very unDRY, I do.

beforeEach(() => TestBed.configureTestingModule(ParentModuleArgs));

And if I need to add something, then we can just add it before configuring the test bed

let moduleArgs: NgModule = ParentModuleArgs;
moduleArgs.providers.push({provide: APP_BASE_HREF, useValue: '/'});

beforeEach(() => TestBed.configureTestingModule(ParentModuleArgs));
Gemmell answered 19/6, 2019 at 13:58 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.