angular Jasmine - cannot set property of undefined

12,800

Solution 1

I'm not sure why you're using provide and useClass to provide this service here. That's generally done when you have to create a custom/lightweight implementation of a service on your own(For instance, when you have things like Angular's Router and ActivatedRoute injected as a dependency on you controller).

For the current scenario though, you just need a reference to the injected dependency for a service that you have created.

So you can simply use the providers array to provide it to your testing module, like this:

beforeEach(async(() => {
  TestBed.configureTestingModule({
    declarations: [ ModalComponent ],
    providers: [ GridService ]
  })
  .compileComponents();
}));

And then you can get a reference to the GridService by using fixture.debugElement.injector.get(GridService); and test your save method like this:

describe('save', () => {

  it('should set gridCellUpdated on GridService to false', () => {

    let gridService = fixture.debugElement.injector.get(GridService);
    expect(gridService.gridCellUpdated).toBeTruthy();

    component.save();

    expect(gridService.gridCellUpdated).toBeFalsy();

  });

});

Update If you still have to use the provide way of providing your service, use useClass instead of use. Something like this:

beforeEach(async(() => {
  TestBed.configureTestingModule({
    declarations: [ ModalComponent ],
    providers: [ { provide: GridService, useClass: GridServiceMock } ]
  })
  .compileComponents();
}));

And to write the test the same way as told. It should work.

UPDATE Here's the whole test file, just for your reference:

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

import { GridService } from './../../services/grid.service';
import { ModalComponent } from './modal.component';

class GridServiceMock {
  public gridCellUpdated = false;
}

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

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [ ModalComponent ],
      providers: [ { provide: GridService, useClass: GridServiceMock } ]
    })
    .compileComponents();
  }));

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

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

  describe('save', () => {

    it('should set gridCellUpdated on GridService to false', () => {

      let gridService = fixture.debugElement.injector.get(GridService);
      expect(gridService.gridCellUpdated).toBeTruthy();

      component.save();

      expect(gridService.gridCellUpdated).toBeFalsy();

    });

  });

});

And the tests are passing:

Passing Tests

Hope that helps!

Solution 2

as @Vega mentioned in his comment: the service is not a property of the component. It will be injected.

Your setup of the Testing Module is correct and you can get the service from the Testbed like this:

let gridService: GridService;
let fixture: ComponentFixture<ModalComponent>;
let component: ModalComponent;

TestBed.configureTestingModule({...});

TestBed.compileComponents().then(() => {
  fixture = TestBed.createComponent( ModalComponent ); 
  component = fixture.debugElement.componentInstance;
  gridService= fixture.debugElement.injector.get( GridService );
} );

It will get an instance of the GridService as GridServiceMock. Your test should success now.

Regards

Share:
12,800
Ish
Author by

Ish

Updated on June 14, 2022

Comments

  • Ish
    Ish almost 2 years

    My component code:

    public save() {
      this.gridService.gridCellUpdated = false;
    }
    

    My Jasmine code

    class GridServiceMock {
      public gridCellUpdated = false;
    }
    let gridService: GridService;
    beforeEach(
      async(() => {
        TestBed.configureTestingModule({
          declarations: [ModalComponent, BsModalDirective],
          providers: [{ provide: GridService, use: GridServiceMock }]
        })
        .compileComponents();
      });
    );
    
    it('should save assets', () => {
      gridService = fixture.debugElement.injector.get(GridService);
      component.save();
      expect(gridService.gridCellUpdated).toBeFalsy();
    });
    

    My error:

    TypeError: Cannot set property 'gridCellUpdated' of undefined
    

    Edit: Updated the code by following below solutions but still get the same error.

    • Ish
      Ish over 6 years
      What is the reason for the downvote ?
    • Mihailo
      Mihailo over 6 years
      I'm not sure but I disagree with them so I'll null it out...
    • Mihailo
      Mihailo over 6 years
      Have you tried injecting it into your test? inject([GridService], (gridService: GridService) ?
    • Vega
      Vega over 6 years
      I think it's related to the fact that gridService is not component property.
  • Ish
    Ish over 6 years
    Did the same way like you said. but the I am getting the same error.
  • Ish
    Ish over 6 years
    followed this way too but the error remains the same.
  • SiddAjmera
    SiddAjmera over 6 years
    In your question you're referring to your service via your controller(component.gridService.gridCellUpdated). Did you update that? You should check again. Probably something's still missing.
  • Ish
    Ish over 6 years
    Please check my new code. I updated by following the solutions.
  • SiddAjmera
    SiddAjmera over 6 years
    You're still using the providers: [{ provide: GridService, use: GridServiceMock }] to provide your service. I've specified not to do that.
  • Ish
    Ish over 6 years
    Isn't there any way where I could use the mocked service? that's what I am been asked to do
  • SiddAjmera
    SiddAjmera over 6 years
    You should be asking them as to why they're telling you to do it this way. I've updated my answer anyway. It should work with the latest updated answer that I provided.
  • SiddAjmera
    SiddAjmera over 6 years