0xdw
0xdw

Reputation: 3842

Angular testing - TypeError: Cannot read property of underfined

I'm not much familiar with Angular Unit testing. I have been working on this issue for almost a day. Please tell me why this coming and how to solve this issue? There are more issues similar to this one.

I already referred to this one, which is not helped. Angular testing Cannot read property 'name' of undefined

I found that the error is occurring inside the addRole function in add-role.component.ts.

Error

TypeError: Cannot read property 'roleName' of undefined
    at AddRoleComponent_Template (ng:///AddRoleComponent.js:59:52)
    at executeTemplate (node_modules/@angular/core/__ivy_ngcc__/fesm2015/core.js:9545:1)
    at refreshView (node_modules/@angular/core/__ivy_ngcc__/fesm2015/core.js:9414:1)
    at refreshComponent (node_modules/@angular/core/__ivy_ngcc__/fesm2015/core.js:10580:1)
    at refreshChildComponents (node_modules/@angular/core/__ivy_ngcc__/fesm2015/core.js:9211:1)
    at refreshView (node_modules/@angular/core/__ivy_ngcc__/fesm2015/core.js:9464:1)
    at renderComponentOrTemplate (node_modules/@angular/core/__ivy_ngcc__/fesm2015/core.js:9528:1)
    at tickRootContext (node_modules/@angular/core/__ivy_ngcc__/fesm2015/core.js:10754:1)
    at detectChangesInRootView (node_modules/@angular/core/__ivy_ngcc__/fesm2015/core.js:10779:1)
    at RootViewRef.detectChanges (node_modules/@angular/core/__ivy_ngcc__/fesm2015/core.js:22792:1)
TypeError: Cannot read property 'roleName' of undefined
    at AddRoleComponent.addRole (src/app/components/admin/add-role/add-role.component.ts:53:29)
    at <Jasmine>
    at UserContext.<anonymous> (src/app/components/admin/add-role/add-role.component.spec.ts:60:17)
    at ZoneDelegate.invoke (node_modules/zone.js/dist/zone-evergreen.js:364:1)
    at ProxyZoneSpec.push.QpwO.ProxyZoneSpec.onInvoke (node_modules/zone.js/dist/zone-testing.js:292:1)
    at ZoneDelegate.invoke (node_modules/zone.js/dist/zone-evergreen.js:363:1)

Here are the lines that the errors are coming,

add-role.component.ts:53 -> const claimValue = role.roleName;
add-role.component.spec.ts:60 -> component.addRole();

add-role.component.ts

import {Component, OnInit} from '@angular/core';
import {FormBuilder, FormGroup, Validators} from '@angular/forms';
import {ActivatedRoute, Router} from '@angular/router';
import {IRole} from 'src/app/models/role.model';
import {AlertService} from 'src/app/services/alert-service/alert-service.service';
import {AuthService} from 'src/app/services/auth/auth.service';
import {ClaimsService} from 'src/app/services/claims/claims.service';

@Component({
  selector: 'app-add-role',
  templateUrl: './add-role.component.html',
  styleUrls: ['./add-role.component.scss']
})
export class AddRoleComponent implements OnInit {
  addRoleForm: FormGroup;
  roleName?: string;
  role?: IRole;

  constructor(
    private activatedRoute: ActivatedRoute,
    private authService: AuthService,
    private router: Router,
    private formBuilder: FormBuilder,
    private claimService: ClaimsService,
    private alertService: AlertService
  ) {
    this.buildForm();
  }

  get formControls() {
    return this.addRoleForm.controls;
  }

  onDataLoadFailed(errors: any): void {
  }

  onDataLoadSuccessful(results: any): void {
    this.role = results;
  }

  ngOnInit(): void {
  }

  addRole() {
    const role = this.addRoleForm.value;
    const claimValue = role.roleName;
    this.claimService.addRole(claimValue).subscribe(
      data => this.onSuccess(data),
      error => this.handleError(error),
      () => this.onComplete()
    );
  }

  onSuccess(data): void {
    this.alertService.success(data.key, data.message);
  }

  handleError(errors): void {
    this.alertService.errors(errors);
  }

  onComplete(): void {
  }

  private buildForm() {
    this.addRoleForm = this.formBuilder.group({
      roleName: ['', [Validators.required]],
    });
  }

}


add-role.component.spec.ts

import {ComponentFixture, TestBed} from '@angular/core/testing';
import {NO_ERRORS_SCHEMA} from '@angular/core';
import {FormBuilder} from '@angular/forms';
import {ActivatedRoute, Router} from '@angular/router';
import {AlertService} from 'src/app/services/alert-service/alert-service.service';
import {AuthService} from 'src/app/services/auth/auth.service';
import {ClaimsService} from 'src/app/services/claims/claims.service';
import {AddRoleComponent} from './add-role.component';

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

  beforeEach(() => {
    const formBuilderStub = () => ({group: object => ({})});
    const activatedRouteStub = () => ({});
    const routerStub = () => ({});
    const alertServiceStub = () => ({
      success: (key, message) => ({}),
      errors: errors => ({})
    });
    const authServiceStub = () => ({});
    const claimsServiceStub = () => ({
      addRole: claimValue => ({subscribe: f => f({})})
    });
    TestBed.configureTestingModule({
      schemas: [NO_ERRORS_SCHEMA],
      declarations: [AddRoleComponent],
      providers: [
        {provide: FormBuilder, useFactory: formBuilderStub},
        {provide: ActivatedRoute, useFactory: activatedRouteStub},
        {provide: Router, useFactory: routerStub},
        {provide: AlertService, useFactory: alertServiceStub},
        {provide: AuthService, useFactory: authServiceStub},
        {provide: ClaimsService, useFactory: claimsServiceStub}
      ]
    });
    fixture = TestBed.createComponent(AddRoleComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  afterEach(() => {
    fixture.destroy();
  });

  it('can load instance', () => {
    expect(component).toBeTruthy();
  });

  describe('addRole', () => {
    it('makes expected calls', () => {
      const claimsServiceStub: ClaimsService = fixture.debugElement.injector.get(
        ClaimsService
      );
      spyOn(component, 'onSuccess').and.callThrough();
      spyOn(component, 'handleError').and.callThrough();
      spyOn(component, 'onComplete').and.callThrough();
      spyOn(component, 'addRole').and.callThrough();
      component.addRole();
      expect(component.onSuccess).toHaveBeenCalled();
      expect(component.handleError).toHaveBeenCalled();
      expect(component.onComplete).toHaveBeenCalled();
      expect(component.addRole).toHaveBeenCalled();
    });
  });
});

Upvotes: 0

Views: 2928

Answers (2)

Clyde
Clyde

Reputation: 93

If it is about a property of an object which is used in an html template, make sure to check for undefined...

e.g. <some-component *ngIf="someObject.property === somevalue" .. make sure to check the object is defined, like this: <some-component *ngIf="someObject?.property === somevalue" .

Upvotes: 0

Nirmalya Roy
Nirmalya Roy

Reputation: 713

 addRole() {
    const role = this.addRoleForm.value;  <---- this line creating issue
    const claimValue = role.roleName;
    this.claimService.addRole(claimValue).subscribe(
      data => this.onSuccess(data),
      error => this.handleError(error),
      () => this.onComplete()
    );
  }

to fixed that you need to do this -->

  let component: AddRoleComponent;
  let fixture: ComponentFixture<AddRoleComponent>;
  beforeEach(() => {
    const formBuilderStub = () => ({group: object => ({})});
    const activatedRouteStub = () => ({});
    const routerStub = () => ({});
    const alertServiceStub = () => ({
      success: (key, message) => ({}),
      errors: errors => ({})
    });
    const authServiceStub = () => ({});
    const claimsServiceStub = () => ({
      addRole: claimValue => ({subscribe: f => f({})})
    });
    TestBed.configureTestingModule({
      schemas: [NO_ERRORS_SCHEMA],
      declarations: [AddRoleComponent],
      providers: [
        {provide: FormBuilder, useFactory: formBuilderStub},
        {provide: ActivatedRoute, useFactory: activatedRouteStub},
        {provide: Router, useFactory: routerStub},
        {provide: AlertService, useFactory: alertServiceStub},
        {provide: AuthService, useFactory: authServiceStub},
        {provide: ClaimsService, useFactory: claimsServiceStub}
      ]
    });
    fixture = TestBed.createComponent(AddRoleComponent);


    component = fixture.componentInstance;
// newly added line
    component.addRoleForm = component.formBuilder.group({
      roleName: ['', [Validators.required]],
    }); 
    fixture.detectChanges();
  });

please check the line after "newly added line" comment

Upvotes: 1

Related Questions