24

This weird error, that I don't remember seeing pre-Ivy, has popped up in the setup of a new app created with the latest Angular 9 CLI. Looks like it could be a bug with Ivy? Any ideas on how to fix would be greatly appreciated.

Error result from ng test

Error: This constructor was not compatible with Dependency Injection.
    at Module.ɵɵinvalidFactory (http://localhost:9876/_karma_webpack_/node_modules/@angular/core/__ivy_ngcc__/fesm5/core.js:14150:1)
    at Object.Router_Factory [as factory] (http://localhost:9876/_karma_webpack_/node_modules/@angular/router/__ivy_ngcc__/fesm5/router.js:4404:67)
    at R3Injector.push../node_modules/@angular/core/__ivy_ngcc__/fesm5/core.js.R3Injector.hydrate (http://localhost:9876/_karma_webpack_/node_modules/@angular/core/__ivy_ngcc__/fesm5/core.js:11425:1)
    at R3Injector.push../node_modules/@angular/core/__ivy_ngcc__/fesm5/core.js.R3Injector.get (http://localhost:9876/_karma_webpack_/node_modules/@angular/core/__ivy_ngcc__/fesm5/core.js:11247:1)
    at injectInjectorOnly (http://localhost:9876/_karma_webpack_/node_modules/@angular/core/__ivy_ngcc__/fesm5/core.js:787:1)
    at ɵɵinject (http://localhost:9876/_karma_webpack_/node_modules/@angular/core/__ivy_ngcc__/fesm5/core.js:792:1)
    at Object.AuthService_Factory [as factory] (ng:///AuthService/ɵfac.js:5:39)
    at R3Injector.push../node_modules/@angular/core/__ivy_ngcc__/fesm5/core.js.R3Injector.hydrate (http://localhost:9876/_karma_webpack_/node_modules/@angular/core/__ivy_ngcc__/fesm5/core.js:11425:1)
    at R3Injector.push../node_modules/@angular/core/__ivy_ngcc__/fesm5/core.js.R3Injector.get (http://localhost:9876/_karma_webpack_/node_modules/@angular/core/__ivy_ngcc__/fesm5/core.js:11247:1)
    at NgModuleRef$1.push../node_modules/@angular/core/__ivy_ngcc__/fesm5/core.js.NgModuleRef$1.get (http://localhost:9876/_karma_webpack_/node_modules/@angular/core/__ivy_ngcc__/fesm5/core.js:24218:1)

Error: Expected undefined to be truthy.
    at <Jasmine>
    at UserContext.<anonymous> (http://localhost:9876/_karma_webpack_/src/app/shared/components/header/header.component.spec.ts:25:23)
    at ZoneDelegate.invoke (http://localhost:9876/_karma_webpack_/node_modules/zone.js/dist/zone.js:396:1)
    at ProxyZoneSpec.onInvoke (http://localhost:9876/_karma_webpack_/node_modules/zone.js/dist/zone-testing.js:305:1)

app.component.ts

import { Component, HostBinding } from '@angular/core';
import { AuthService, ScreenService, AppInfoService } from './shared/services';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss'],
})
export class AppComponent {
  @HostBinding('class') get getClass() {
    return Object.keys(this.screen.sizes)
      .filter(cl => this.screen.sizes[cl])
      .join(' ');
  }

  constructor(
    private authService: AuthService,
    private screen: ScreenService,
    public appInfo: AppInfoService,
  ) {}

  isAuthorized() {
    return this.authService.isLoggedIn;
  }
}

app.component.spec.ts

import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { Router } from '@angular/router';

import { AppComponent } from './app.component';
import { AuthService } from './shared/services';

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

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [AppComponent],
      providers: [AuthService, Router],
    }).compileComponents();
  }));

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

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

package.json dependencies

  "dependencies": {
    "@angular/animations": "~9.0.2",
    "@angular/cdk": "^8.0.0",
    "@angular/common": "~9.0.2",
    "@angular/compiler": "~9.0.2",
    "@angular/core": "~9.0.2",
    "@angular/forms": "~9.0.2",
    "@angular/platform-browser": "~9.0.2",
    "@angular/platform-browser-dynamic": "~9.0.2",
    "@angular/router": "~9.0.2",
    "devextreme": "latest",
    "devextreme-angular": "latest",
    "rxjs": "~6.5.4",
    "tslib": "^1.10.0",
    "zone.js": "~0.10.2"
  },
  "devDependencies": {
    "@angular-devkit/build-angular": "~0.900.3",
    "@angular/cli": "~9.0.3",
    "@angular/compiler-cli": "~9.0.2",
    "@angular/language-service": "~9.0.2",
    "@types/jasmine": "~3.5.0",
    "@types/jasminewd2": "~2.0.3",
    "@types/node": "^12.11.1",
    "codelyzer": "^5.1.2",
    "devextreme-cli": "latest",
    "devextreme-themebuilder": "latest",
    "husky": "^4.2.3",
    "jasmine-core": "~3.5.0",
    "jasmine-spec-reporter": "~4.2.1",
    "karma": "~4.3.0",
    "karma-chrome-launcher": "~3.1.0",
    "karma-coverage-istanbul-reporter": "~2.1.0",
    "karma-jasmine": "~2.0.1",
    "karma-jasmine-html-reporter": "^1.4.2",
    "prettier": "^1.19.1",
    "pretty-quick": "^2.0.1",
    "protractor": "~5.4.3",
    "ts-node": "~8.3.0",
    "tslint": "~5.18.0",
    "tslint-config-prettier": "^1.18.0",
    "typescript": "~3.7.5",
    "webpack-bundle-analyzer": "^3.6.0"
  }

NOTE: this error appeared after fixing NullInjectorError for both AuthService & Router.

Andrew
  • 1,406
  • 1
  • 15
  • 23
  • Probably a missing `@Injectable()` on any of the services you inject there. With ivy all services need to have this decorator now, regardless if the service in question inject something in its constructor – Poul Kruijt Feb 26 '20 at 08:12
  • Doesn't appear so. All services have `@Injectable()`. – Andrew Feb 26 '20 at 08:20

5 Answers5

53

You set a Router as provider in your test. That's not the way to do that.

You should import the RouterTestingModule in your test to be able to access it.

beforeEach(async(() => {
  TestBed.configureTestingModule({
    imports: [RouterTestingModule],
    declarations: [AppComponent],
    providers: [AuthService],
  }).compileComponents();
}));
I guess with ivy and/or angular 9, they declared the `providedIn` from the router service in such a way that you cannot provide it in another module anymore (which you should never do anyways :))

Background info:

Looking at the source code, you can see they inject the router in a special way using a factory. The router class itself is just a normal class with no @Injectable() decorator.

Roy
  • 7,811
  • 4
  • 24
  • 47
Poul Kruijt
  • 69,713
  • 12
  • 145
  • 149
  • 1
    Thank you sir! Much appreciated! Should've checked for that considering it doesn't have `Service` on the end. – Andrew Feb 27 '20 at 02:10
14

Another source of this error (not related to the OP's question) can be when you have a constructor param with a default arg such as:

constructor(someProp: string = 'propVal') {
    this.someProp = someProp;
}

You can resolve it by doing this instead:
constructor(@Inject('propVal') @Optional() someProp: string) {
    this.someProp = someProp;
}
derekbaker783
  • 8,109
  • 4
  • 36
  • 50
7

This error can also be produced if you accidentally try to inject an interface instead of a service, lol.

example.interface.ts

export interface ExampleInterface {
  id: string
}

example.service.ts

import { Injectable } from '@angular/core'

@Injectable({
  providedIn: 'root'
})
export class ExampleService {
  constructor() { }
}

dependent.service.ts

import { Injectable } from '@angular/core'

@Injectable({
  providedIn: 'root'
})
export class DependentService {
  constructor(
    private example: ExampleInterface // This is the error; should be ExampleService
  ) { }
}
Trevor
  • 13,085
  • 13
  • 76
  • 99
3

If angular build was in --watch mode, try to restart it.

MarkosyanArtur
  • 1,359
  • 13
  • 10
  • Funnily enough, when using Angular 11.2.8, this fixed my issue! Cheers. – Dale May 24 '21 at 10:08
  • This action has fixed countless wierd issues I have had over the years. But, somehow it's the last thing I think of trying. – TranQ Dec 03 '21 at 08:01
0

I'm my case I had a custom module that one developer created in my branch and it was importing the Router in the @ngModule declaration. The problem is that the parent module (App) was already importing the Router from Angular, so it was duplicating the import, which was causing this error.

Hope this helps someone else, just check if your components / modules are not importing / providing / declaring something that is being already declared a upper scope.

V. Nogueira
  • 103
  • 1
  • 4