TypeError: Cannot read properties of undefined (reading '<myvariable>')

33,453

The issue here is, Angular lifecycle method will only be called when you call fixture.detectChanges() and you are initializing emp object inside ngOnInit(). So you can't access emp object properties before calling fixture.detectChanges()

Try below code (view-employee.component.spec.ts):

 beforeEach(() => {
    fixture = TestBed.createComponent(ViewEmployeeComponent);
    fixture.detectChanges();
    component = fixture.debugElement.componentInstance;
    component.emp = new Employee();
    component.emp.id =1;
    component.emp.dateOfLeaving ="Today";
  });

Also I assume that this.employeeService.selectedEmployee would at least return empty object else you will have to mock EmployeeService as well.

service -

...
export class EmployeeService {
  employees:Array<Employee> =[];
  selectedEmployee:Employee = <Employee>{};
...
Share:
33,453

Related videos on Youtube

Vineel Pellella
Author by

Vineel Pellella

Interests in learning, Art, Fashion, Business and much more

Updated on July 09, 2022

Comments

  • Vineel Pellella
    Vineel Pellella almost 2 years

    While running the ng test in one of the components I am facing the below error.

    TypeError: Cannot read properties of undefined (reading 'dateOfLeaving')
    error properties: Object({ ngDebugContext: DebugContext_({ view: Object({ def: Object({ factory: Function, nodeFlags: 33669121, rootNodeFlags: 33554433, nodeMatchedQueries: 0, flags: 0, nodes: [ Object({ nodeIndex: 0, parent: null, renderParent: null, bindingIndex: 0, outputIndex: 0, checkIndex: 0, flags: 33554433, childFlags: 114688, directChildFlags: 114688, childMatchedQueries: 0, matchedQueries: Object({  }), matchedQueryIds: 0, references: Object({  }), ngContentIndex: null, childCount: 1, bindings: [  ], bindingFlags: 0, outputs: [  ], element: Object({ ns: '', name: 'app-view-employee', attrs: [  ], template: null, componentProvider: Object({ nodeIndex: 1, parent: <circular reference: Object>, renderParent: <circular reference: Object>, bindingIndex: 0, outputIndex: 0, checkIndex: 1, flags: 114688, childFlags: 0, directChildFlags: 0, childMatchedQueries: 0, matchedQueries: Object, matchedQueryIds: 0, references: Object, ngContentIndex: -1, childCount: 0, bindings: Array, bindingFlags: 0, outputs ...
    TypeError: Cannot read properties of undefined (reading 'dateOfLeaving')
        at ViewEmployeeComponent.ngOnInit (http://localhost:9876/_karma_webpack_/webpack:/src/app/employee/view/view-employee/view-employee.component.ts:22:17)
        at checkAndUpdateDirectiveInline (http://localhost:9876/_karma_webpack_/webpack:/node_modules/@angular/core/fesm2015/core.js:24503:1)
        at checkAndUpdateNodeInline (http://localhost:9876/_karma_webpack_/webpack:/node_modules/@angular/core/fesm2015/core.js:35163:1)
        at checkAndUpdateNode (http://localhost:9876/_karma_webpack_/webpack:/node_modules/@angular/core/fesm2015/core.js:35102:1)
        at debugCheckAndUpdateNode (http://localhost:9876/_karma_webpack_/webpack:/node_modules/@angular/core/fesm2015/core.js:36124:36)
        at debugCheckDirectivesFn (http://localhost:9876/_karma_webpack_/webpack:/node_modules/@angular/core/fesm2015/core.js:36067:1)
        at Object.eval [as updateDirectives] (ng:///DynamicTestModule/ViewEmployeeComponent_Host.ngfactory.js:10:5)
        at Object.debugUpdateDirectives [as updateDirectives] (http://localhost:9876/_karma_webpack_/webpack:/node_modules/@angular/core/fesm2015/core.js:36055:1)
        at checkAndUpdateView (http://localhost:9876/_karma_webpack_/webpack:/node_modules/@angular/core/fesm2015/core.js:35067:1)
        at callWithDebugContext (http://localhost:9876/_karma_webpack_/webpack:/node_modules/@angular/core/fesm2015/core.js:36407:1)
    

    employee.ts

    export class Employee
    {
    
        
        id:number;
        name:string;
        dateOfBirth:string;
        designation:string;
        dateOfJoining:string;
        workLocation:string;
        email:string;
        contactNo:number;
        dateOfLeaving:string
    
        constructor(){}
        
        static getEmployee(id:number,
            name:string,
            dob:string,
            designation:string,
            dateOfJoining:string,
            workLocation:string,
            email:string,
            contactNo:number,
            dateOfLeaving:string
        ) :Employee
        {
            let emp = new Employee();
            emp.id = id;
            emp.name = name;
            emp.dateOfBirth = dob;
            emp.designation =designation;
            emp.dateOfJoining = dateOfJoining;
            emp.workLocation = workLocation;
            emp.email = email;
            emp.contactNo =contactNo;
            emp.dateOfLeaving = dateOfLeaving
            return emp;
        }
    
    } 
    

    view-component.ts

    import { Component, OnInit } from '@angular/core';
    import { CommonService } from 'src/app/service/common.service';
    import { Employee } from '../../employee';
    import { EmployeeService } from '../../employee.service';
    
    @Component({
      selector: 'app-view-employee',
      templateUrl: './view-employee.component.html',
      styleUrls: ['./view-employee.component.css']
    })
    export class ViewEmployeeComponent implements OnInit {
    
      constructor(private employeeService: EmployeeService, private genericServices: CommonService) { }
    
      emp:Employee; 
      availableLocations: string[] = [];
      msg:string;
    
      ngOnInit() 
      {
        this.emp = this.employeeService.selectedEmployee;
        if(this.emp.dateOfLeaving == null)
        {
          this.emp.dateOfLeaving = '';
        }
        this.genericServices.getWorkLocations().subscribe(
        
          (resp:string[]) =>
          {
            this.availableLocations = this.availableLocations.concat( resp);
          },
          (err: any) => 
          {
            console.log("err" + err)
          }
    
        );
      }
    
    
      saveEmployee()
      {
        this.employeeService.updateEmployee(this.emp).subscribe(
          (resp) =>
          {
            if(resp['status'] ==200)
            {
              let existingEmpIndex = this.employeeService.employees.findIndex((exitsEmp) =>  this.emp.id == exitsEmp.id);
              this.employeeService.employees[existingEmpIndex] = this.emp;
              this.msg = resp['message'];
            }
          },
          (errMsg) =>
          {
            this.msg = errMsg['error']['errorMessage'];
          }
        )
      }
    }
    

    view-employee.component.spec.ts

    import { HttpClientModule } from '@angular/common/http';
    import { async, ComponentFixture, TestBed } from '@angular/core/testing';
    import { FormsModule } from '@angular/forms';
    import { CommonService } from 'src/app/service/common.service';
    import { Employee } from '../../employee';
    import { EmployeeService } from '../../employee.service';
    
    import { ViewEmployeeComponent } from './view-employee.component';
    
    describe('ViewEmployeeComponent', () => {
      let component: ViewEmployeeComponent;
      let fixture: ComponentFixture<ViewEmployeeComponent>;
    
      beforeEach(async(() => {
        TestBed.configureTestingModule({
          declarations: [ ViewEmployeeComponent ],
          imports: [FormsModule, HttpClientModule],
          providers: [EmployeeService, CommonService]
        })
        .compileComponents();
      }));
    
      beforeEach(() => {
        fixture = TestBed.createComponent(ViewEmployeeComponent);
        component = fixture.debugElement.componentInstance;
        component.emp = new Employee();
        component.emp.id =1;
        component.emp.dateOfLeaving ="Today";
        fixture.detectChanges();
      });
    
      it('should create', () => {
        fixture.whenStable().then( ()=> {
          fixture.detectChanges();
          
          // expect(component).toBeTruthy();
        })
      });
    });
    

    employee-service.ts

    import { HttpClient } from '@angular/common/http';
    import { Injectable } from '@angular/core';
    import { env } from 'process';
    import { environment } from 'src/environments/environment';
    import { URLMapping } from '../shared/URLMapping';
    import { Employee } from './employee';
    
    @Injectable({
      providedIn: 'root'
    })
    export class EmployeeService {
      
    
      employees:Array<Employee> =[];
      selectedEmployee:Employee;
    
      constructor(private httpClient: HttpClient)
      { }
    
      fetchEmployee(emp:Employee)
      {
        console.log(emp)
        // return this.httpClient.post(environment.applicationURL + URLMapping.EMPLOYEE_VIEW, emp);
        return this.selectedEmployee = emp;
        
      }
    
      createEmployee(emp:Employee)
      {
        return this.httpClient.post(environment.applicationURL + URLMapping.EMPLOYEE_ADD, emp);
      }
    
      getEmployees()
      {
        
        return this.httpClient.post(environment.applicationURL + URLMapping.EMPLOYEE_LIST, {});
      }
    
      updateEmployee(emp:Employee)
      {
        return this.httpClient.post(environment.applicationURL + URLMapping.EMPLOYEE_SAVE, emp);
      }
    
      deleteEmployee(employees: Employee[]) {
        return this.httpClient.post(environment.applicationURL + URLMapping.EMPLOYEE_DELETE, employees);
        
      }
    }
    

    Facing the same issue for the id attribute as well. Any references or solutions are greatly appreciated.

  • Vineel Pellella
    Vineel Pellella over 2 years
    Still getting the same error. I even tried the below code for selectedEmployee object as well for mocking at beforeEach. spyOnProperty(employeeService, 'selectedEmployee', 'set').and.returnValue(new Employee());
  • Suneet Bansal
    Suneet Bansal over 2 years
    That means your service is not returning empty object and your emp object is not getting initialised properly. Check you have to then mock service properly as I explained above. Attach your service then will add the mock code above.
  • Vineel Pellella
    Vineel Pellella over 2 years
    I am not very clear with mocks. I included the employee service as well. Can you please check once again.
  • Suneet Bansal
    Suneet Bansal over 2 years
    you don't even need to add mock, just update the service class as I mentioned in the edited post.
  • Vineel Pellella
    Vineel Pellella over 2 years
    setting the selectEmployee in service resolved this issue. Thanks a lot.
  • Suneet Bansal
    Suneet Bansal over 2 years
    Always welcome!