Cristian
Cristian

Reputation: 1670

Invalid provider for the NgModule 'DynamicTestModule' when testing a service in Angular 2

I have the following service:

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

import { MenuItem } from './../classes/menu-item';
import { ITEMS } from './../static-data/items-list';

@Injectable()
export class ItemsListService {

    getItems(): Promise<MenuItem[]> {
        return Promise.resolve(ITEMS);
    }

}

The test for this service is here:

import { TestBed, async, inject } from '@angular/core/testing';

import { ItemListService } from './item-list.service';
import { MenuItem } from './../classes/menu-item';
import { ITEMS } from './../static-data/items-list';

describe('ItemListService', () => {
  beforeEach(() => {
    TestBed.configureTestingModule({
        providers: [ ItemListService, MenuItem, ITEMS ]
    });
  });

  it('should ...', inject([ItemListService], (service: ItemListService) => {
    expect(service).toBeTruthy();
  }));
});

The MenuItem is defined here:

export class MenuItem {
    name: string;
    link: string;
}

ITEMS is defined here: import { MenuItem } from './../classes/menu-item';

export var ITEMS: MenuItem[] = [
    {name: 'Vehicles', link: '/vehicles'},
    {name: 'Gateways', link: '/gateways'},
    {name: 'Statuses', link: '/statuses'},
    {name: 'Logs', link: '/logs'}
]

When I run the test I am getting in the browsers console the followings errors:

FAILED ItemListService should ...

and

enter image description here

So why do I have these errors? And what is the solution for the test to work?

Upvotes: 33

Views: 32015

Answers (7)

CSSBurner
CSSBurner

Reputation: 1991

I'm shocked no one observed this same error due to an end-of-the-workday mental mistake of incorrectly placing the RouterTestingModule within the providers of the TestBed configuration INSTEAD OF correctly within imports. In other words, within your TestBed configuration:

// Here RouterTestingModule is CORRECTLY placed within imports
imports: [
    RouterTestingModule.withRoutes(
      [{
        path: 'my/route',
        component: MyComponent
      }]
    )
  ], ...

versus this incorrect placement:

// Here RouterTestingModule is INCORRECTLY placed within providers
providers: [
    provideMockStore({}),
    ...
    RouterTestingModule.withRoutes(
      [{
        path: 'my/route',
        component: MyComponent
      }]
    )
  ]

Upvotes: 1

Pranam Bhat
Pranam Bhat

Reputation: 65

It is because of camelCase inside providers [..] array.

Correct way:

beforeEach(() => {
        providers: [TestService, ...]
    }
);

Upvotes: 0

Sivashankar
Sivashankar

Reputation: 544

I got this error, because of an extra comma in the providers array.

Upvotes: 0

rklec
rklec

Reputation: 177

BTW also mind to use the class in providers, not some variable. This happened to me due to an accidental problematic replacement/casing:

Correct

 TestBed.configureTestingModule({
    // ...
    providers: [ SomeService ]
}

instead of...

Incorrect

 TestBed.configureTestingModule({
    // ...
    providers: [ someService ]
}

Note the camelCase variable (someService) is likely there if you use it in your test, that's why it does not throw a syntax error.

Upvotes: 2

Craig
Craig

Reputation: 1403

This is such an annoying error, thought I'd include another subtle cause to look for in your spec. In my case I specified providers instead of the correct value of provide as below

 TestBed.configureTestingModule({
      providers: [{provider: ApplicationActions, useClass: ActionMock}]

rather than offer useful information like "no 'provide' key specified" it simply reports

Failed: Invalid provider for the NgModule 'DynamicTestModule' - only instances of Provider and Type are allowed, got: [?[object Object]?, ...]

Upvotes: 98

Kirk Sefchik
Kirk Sefchik

Reputation: 813

In my case, I had a stray comma in one of my provider lines, causing the DynamicTestModule to think I had passed an undefined definition.

        {
          provide: ApiService,
          useValue: {
            getUsers: jasmine
              .createSpy('getUsers')
              .and.returnValue(of({ status: 200, body: [] })),
          },
        },
        , // whoops!
        MessageService,
        { provide: Location, useValue: { back: jasmine.createSpy('back') } },

Upvotes: 1

DimaE
DimaE

Reputation: 1

I have same problem, when importing in my Ionic Framework project like this:

import {Device} from '@ionic-native/device'

instead of:

import {Device} from '@ionic-native/device/ngx'

Upvotes: -2

Related Questions