Angular 4 unit test error "TypeError: Cannot read property 'subscribe' of undefined"

11,108

After some research I actually figured out what caused the error. I removed

{provide: Router, useClass: RouterStub},

and error was gone. Test is now working perfectly without this stub.

Not sure what was the problem, but I assume that RouterStub class from docs is missing implementation of some necessary things. Class was taken from here:

https://angular.io/guide/testing#test-a-routed-component

https://angular.io/generated/live-examples/testing/app-specs.eplnkr.html

Share:
11,108

Related videos on Youtube

Anton Belonovich
Author by

Anton Belonovich

As a programmer, I like to write backends for web sites and applications in PHP frameworks and Python/Django. Also work with Magento.

Updated on May 25, 2022

Comments

  • Anton Belonovich
    Anton Belonovich about 2 years

    I am stuck and keep getting error while unit testing an Angular 4 component.

    Here's my component:

    order-detail.component.ts

    @Component({
      selector: 'order-detail',
      templateUrl: 'order-detail.component.html',
      providers: [OrderService]
    })
    export class OrderDetailComponent implements OnInit {
      private order: Order;
    
      constructor(
        private orderService: OrderService,
        private route: ActivatedRoute,
        private router: Router
      ) {}
    
      ngOnInit() {
        this.getOrder();
      }
    
      private getOrder(): void {
        this.route.paramMap
          .map((params: ParamMap) => {
            if (+params.get('id') === 0) {
              this.router.navigateByUrl('/404', {skipLocationChange: true});
            }
    
            return params;
          })
          .switchMap((params: ParamMap) => this.orderService.getOrder(+params.get('id')))
          .subscribe((order: Order) => this.order = order);
      }
    }
    

    And here is a service:

    order.service.ts

    @Injectable()
    export class OrderService {
      private ordersUrl = `${environment.apiUrl}/orders/`;
    
      constructor(private http: HttpClient) {}
    
      getOrder(id: number): Observable<Order> {
        return this.http.get(`${this.ordersUrl}${id}/`)
          .map(response => response as Order);
      }
    }
    

    And I want to set a spy on OrderServive and unit test it with such test:

    order-detail.component.spec.ts

    describe('Order detail component', () => {
      let fixture: ComponentFixture<OrderDetailComponent>;
      let page: Page;
      let activatedRoute: ActivatedRouteStub;
      const fakeOrder: Order = {
        id: 1,
        title: 'Test 1',
        contractor: {title: 'Contractor 1'} as Contractor,
      } as Order;
    
      class Page {
        orderSpy: jasmine.Spy;
        routerSpy: jasmine.Spy;
        title: HTMLElement;
    
        constructor() {
          const orderService = fixture.debugElement.injector.get(OrderService);
          this.orderSpy = spyOn(orderService, 'getOrder').and.returnValue(Observable.of(fakeOrder));
        }
      }
    
      function createComponent() {
        fixture = TestBed.createComponent(OrderDetailComponent);
        page = new Page();
    
        fixture.detectChanges();
    
        return fixture.whenStable().then(() => {
          fixture.detectChanges();
        });
      }
    
      beforeEach(() => {
        activatedRoute = new ActivatedRouteStub();
      });
    
      beforeEach(async(() => {
        TestBed.configureTestingModule({
          declarations: [OrderDetailComponent],
          imports: [RouterTestingModule, HttpClientTestingModule],
          providers: [
            OrderService,
            {provide: ActivatedRoute, useValue: activatedRoute},
            {provide: Router, useClass: RouterStub},
          ],
          schemas: [NO_ERRORS_SCHEMA],
        }).compileComponents();
      }));
    
      describe('when navigate with existing id', () => {
        beforeEach(async(() => {
          activatedRoute.testParamMap = {id: fakeOrder.id};
          createComponent();
        }));
    
        it('should show title', () => {
          expect(page.orderSpy).toHaveBeenCalledWith(fakeOrder.id);
        });
      });
    
    });
    

    But I keep getting this error: TypeError: Cannot read property 'subscribe' of undefined

    I took ActivatedRouteStub and test structure from Agular documentation: https://angular.io/guide/testing

    Component's code works fine with real data from backend and shows me order.

    Also my codebase contains similar test which successfully does the same thing on another component with similar service, so this test is the only place which fails.

    Using Angular 4.3.6, Karma 1.7.1 and Jasmine 2.6.4.

    What could go wrong and how to get rid of this error?