Search code examples
angularangular-observableangular-injector

Unit Test case to mock Behavior Subject in Store


I have created a store to load address detail in behavior subject

@Injectable({
    providedIn: 'root'
})
export class AddressStore {
    private subjectAddress = new BehaviorSubject<Address[]>([]);
    address$: Observable<Address[]> = this.subjectAddress.asObservable();

    constructor(private addressService: AddressService){
        this.loadAddress();
    }

    private loadAddress(){
        this.addressService.getAddressByType('HOME')
            .subscribe((state => this.subjectAddress.next(state)));
    }
}

I need to mock the data returned by address service in unit test case but its returning undefined error:

describe('Address Data Store', () => {
    let addressServiceSpy = jasmine.createSpyObj('AddressService', [
        'getAddressByType',
    ]);
    let addressService: any;
    let addressStore: AddressStore;

    let mockAddress = [
        {
          type: "HOME",
          houseNumber: "101",
          address: "xyz"
        }
    ];

    beforeEach(() => { 
        TestBed.configureTestingModule({
            imports: [
              RouterTestingModule,
              HttpClientTestingModule
            ],
            providers: [
              {
                provide: AddressService,
                useValue: addressServiceSpy
              },
              AddressStore
            ],
        });
        addressService = TestBed.inject(AddressService);
        addressStore = TestBed.inject(AddressStore);
    });

    it('should be created', fakeAsync(() => {
        addressService.getAddressByType.and.returnValue(of(mockAddress));
        expect(addressStore).toBeTruthy();
        flush();
    }));
})

How I can mock data for subscription of service while writing unit test case for store.


Solution

  • Your AddressStore is calling the AddressService inside the constructor during the loadAddress call. At the time you inject your AddressStore in your test however, you haven't mocked the return value of the getAddressByType method and therefore it doesn't return an observable that can be subscribed to. You have to define a return value for the getAddressByType method mock before you inject your AddressStore.

    Example:

    describe('Address Data Store', () => {
      let addressServiceSpy = jasmine.createSpyObj('AddressService', [
        'getAddressByType',
      ]);
      let addressService: any;
      let addressStore: AddressStore;
    
      let mockAddress = [
        {
          type: "HOME",
          houseNumber: "101",
          address: "xyz"
        }
      ];
    
      beforeEach(() => {
        TestBed.configureTestingModule({
          imports: [
            RouterTestingModule,
            HttpClientTestingModule
          ],
          providers: [
            {
              provide: AddressService,
              useValue: addressServiceSpy
            },
            AddressStore
          ],
        });
        addressService = TestBed.inject(AddressService);
        addressService.getAddressByType.and.returnValue(of(mockAddress));
    
        addressStore = TestBed.inject(AddressStore);
      });
    
      it('should be created', fakeAsync(() => {
        addressService.getAddressByType.and.returnValue(of(mockAddress));
        expect(addressStore).toBeTruthy();
        flush();
      }));
    });