Curtis Lanz
Curtis Lanz

Reputation: 445

TypeError: Cannot read properties of undefined (reading 'id') angular unit test

I am trying to update item, I wrote a function which gets the items from the store and update with new item based on user's selection with this.name and this.price but the same item.id that is intended for update.

   public updateItemData(){
    return this.store.select(itemSelector).subscribe(item => {
        this.updatedItem = {
            id: item.id,
            name: this.name,
            price: this.price
        }
    })
    }

This works perfectly without any issue but in a unit test.

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

It marked green but with displayed error.

TypeError: Cannot read properties of undefined (reading 'id')

When I ran the project, I didn't get any error concerning the "undefined property" or whatever. I started writing the test

Please help to resolve this. I will appreciate.

Upvotes: 1

Views: 3970

Answers (3)

Curtis Lanz
Curtis Lanz

Reputation: 445

All I had to do is to

  if(this.item === undefined) 
        {
            return
        }

So in my component.ts

  public updateItemData(){
    return this.store.select(itemSelector).subscribe(item => {
     if(this.item === undefined) 
        {
            return
        }
        this.updatedItem = {
            id: item.id,
            name: this.name,
            price: this.price
        }
    })
    }

All error disappeared and mocking using overrideSelector was successful.

Upvotes: 0

Andres2142
Andres2142

Reputation: 2897

You could use the provideMockStore provided from ngrx like this:

yourcomponent.component.spec.ts

import { provideMockStore } from '@ngrx/store/testing';

describe('...', () => {
 ...  
 ...
 const myMockItemSelectorValue = {...};

  beforeEach(() => {
    TestBed.configureTestingModule({
      declarations: [...],
      imports: [...],
      providers: [
       { 
         initialState: { items: {...}, users: {...} }, // an initial mock Store
         selectors: [{ selector: itemSelector, value: myMockItemSelectorValue }]
       }
      ]
    })
  })
});

Or create a mock of the store using jasmine like the following:

const mockStore = jasmine.createSpyObj<Store>('Store', ['select']);


beforeEach(() => mockStore.select.and.retunValue(of(TheDesiredMockValue))

beforeEach(() => {
  TestBed.configureTestingModule({
     providers: [{ provide: Store, useValue: mockStore }]
  })
})

Personally, I prefer the first option since I can specify multiple selectors with the respective mock values with an initial mock state.

Upvotes: 1

Sachila Ranawaka
Sachila Ranawaka

Reputation: 41387

Add a spyOn for the store service select. Either inside test or beforeEach

spyOn(store, 'select').and.returnValue(YOUR_VALUE);

make sure to declare store and asiign value inside beforeEach

let store: Store;
store = TestBed.inject(Store); // inside beforeEach

Upvotes: 1

Related Questions