Home > Back-end >  Nested object when using next() in angular tests
Nested object when using next() in angular tests

Time:12-07

I am trying to mock an Observable in orrder to test the insides of the subscribe function (I am using BehaviourSubject)

I have a dataService:

private dataSource = new BehaviorSubject(this.data);
currentData = this.dataSource.asObservable();

nextData(data){
this.dataSource.next(data);
}

and in a component there is a function i want to test

changeSelectedData() {
    this.dataService.currentData.pipe(takeUntil(this.subscription))
    .subscribe(sidebar => {
      if (sidebar.length > 0) {
        (...)
      }
    })
  }

and this is my test configuration:

it('should go inside the if', () => {    
      const selectedData = {
        key: 'host:1', 
        ip: '1',
        group: {name: 'group1'}
      };
      const service = TestBed.inject(DataService)
      const sidebar = [
        { children: [
            {key: 'host:', ip: '1', group: {name: 'group1'}},
            {key: 'host:', ip: '2', group: {name: 'group1'}},
            {key: 'host:', ip: '3', group: {name: 'group1'}}
          ]
        }
      ];
      service.nextSidebarData(sidebar); <-- this line fails
      component.selectedData = selectedData;
      component.changeSelectedData();
    
      expect(component.selectedData).toEqual(something);
    })

when calling the function from a service, which is next() the nested object is lost, and in a result the sidebar looks like this:

sidebar = [
  {children: []}
]

can anyone please help me with this one?

CodePudding user response:

Maybe you need fakeAsync/tick to make the subscription run before your expectation.

Try this:

// !! wrap in fakeAsync
it('should go inside the if', fakeAsync(() => {    
      const selectedData = {
        key: 'host:1', 
        ip: '1',
        group: {name: 'group1'}
      };
      const service = TestBed.inject(DataService)
      const sidebar = [
        { children: [
            {key: 'host:', ip: '1', group: {name: 'group1'}},
            {key: 'host:', ip: '2', group: {name: 'group1'}},
            {key: 'host:', ip: '3', group: {name: 'group1'}}
          ]
        }
      ];
      service.nextSidebarData(sidebar); <-- this line fails
      component.selectedData = selectedData;
      component.changeSelectedData();

      // !! Run tick so subscribe happens
      tick();
    
      expect(component.selectedData).toEqual(something);
    }));

Also, in here, have a log and see what it logs out:

changeSelectedData() {
    this.dataService.currentData.pipe(takeUntil(this.subscription))
    .subscribe(sidebar => {
      console.log(sidebar);
      if (sidebar.length > 0) {
        (...)
      }
    })
  }

This is a good resource in how to debug unit tests: https://testing-angular.com/debugging-tests/

CodePudding user response:

thanks to the response from @AliF50 I managed to get my result. The key was that I did't mock the BehaviourSubject, and so I added this lines inside my test, alongside the tip about fakeAsync() and tick() and it works now.

So here is how the test should look like at the end:

// !! wrap in fakeAsync
it('should go inside the if', fakeAsync(() => {    
      const selectedData = {
        key: 'host:1', 
        ip: '1',
        group: {name: 'group1'}
      };
      const sidebar = [
        { children: [
            {key: 'host:', ip: '1', group: {name: 'group1'}},
            {key: 'host:', ip: '2', group: {name: 'group1'}},
            {key: 'host:', ip: '3', group: {name: 'group1'}}
          ]
        }
      ];
      const service = TestBed.inject(DataService);
      const sidebarSourceMock = service.sidebarDataSource = new BehaviorSubject(sidebar);
      const currentSidebarMock = service.currentSidebarData = sidebarSourceMock.asObservable();
      service.nextSidebarData(sidebar);
      component.selectedData = selectedData;
      component.changeSelectedData();

      // !! Run tick so subscribe happens
      tick();
    
      expect(component.selectedData).toEqual(something);
    }));
  • Related