Jasmine null mocked service property value doesn't change when changed in component tested function

Viewed 41

The service

...
export class MyService {
  ...
  get serviceProperty(){
     return this.browserStorageService.retrieve('value');
  }
  set serviceProperty(value){
     this.browserStorageService.store('value', value);
  }
  ...
}

In the component

...
export Class Component implements OnInit {
   ...
   constructor(private myService: MyService){}
   ngOnInit(){
      subscribeToWhatever();
   }
   private subscribeToWhatever(){
      ...
      if(!this.myService.serviceProperty){
         console.log('before', this.myService.serviceProperty); *//logs null*
         
         this.myService.serviceProperty = 'someWhateverValue';
         
         console.log('and after', this.myService.serviceProperty); *//logs null*
      }
   }
   ...
}

Now in the test I would like to check that the property has the value of someWhateverValue. Or that the setter was called, or that the setter was called with someWhateverValue, but I ran out of ideas, even of what else to google...So this is my test:

 ...
    describe(...()=>{
      let mockMyService: any;
      
      beforeEach(()=>{
        mockMyService = jasmine.createSpyObj('MyService', ['aFunction', 'anotherFumction'], {serviceProperty: null});
        TestBed.configureTestingModule({
          ...
          providers: [..., {provide: MyService, useValue: mockMyService}]
        });
        fixture = TestBed.createComponent(MainComponent);
      })

//ONE VERSION
      it('should check some stuff', ()=>{
         fixture.detectChanges();

         expect(mockMyService.myProperty).toEqual('someWhateverValue'); *//this one says that null doesn't equal 'someWhateverValue'*
      });

//ANOTHER VERSION
      it('should check some stuff', ()=>{
        *//the next line, if I don't remove the property from the spy, it throws the error:
        //Error: <spyOnProperty> : myProperty#get has already been spied upon;
        //however, if I remove it, I get Error: <spyOnProperty> : myProperty property does not exist*
        
        const getter = spyOnProperty(mockMyService, 'myProperty', 'get').and.returnValue(null);
        
        *//if I remove the above line and leave the property in the spy declaration, I get
        //Error: <spyOnProperty> : myProperty#set has already been spied upon, and this,
        //#set has already been spied upon, doesn't even exist on google...*
        
        const setter = spyOnProperty(mockMyService, 'myProperty', 'set');
        fixture.detectChanges();

        expect(setter).toHaveBeenCalled();
      });
    }
    ...

So how can I check that the value of this property has been changed?

LATER EDIT

I created a more simple scenario:

service:

...
export class MyService {
  private _someObj;
  get someObj() {
    return this._someObj;
  }
  set someObj(val) {
    this._someObj = val;
  }
}

component:

  someFunction() {
    console.log('before', this.myService.someObj ); *//logs null*
    this.myService.someObj = { prop1: 'val1', prop2: 'val2' };
    console.log('and after', this.myService.someObj ); *//logs null*
  }

test:

    fit('should create the object', () => {
        (Object.getOwnPropertyDescriptor(mockMyService, 'someObj')?.get as jasmine.Spy<() => any>).and.returnValue(null);
    component.someFunction();
    expect(mockMyService['someObj']).toEqual({ prop1: 'val1', prop2: 'val2' }); 
    *//I get null to equal...*
    });

if instead of making it null, I make it {prop1: '', prop2: ''} and in the controller function I go this.myService.someObj.prop1 = 'val1', it gets set to the correct value. But I need it to be null from the beginning so that I can test the scenario where it gets set; If I set it to null, as in the test above, no matter what I do with it, it stays null at the second log in the component.

1 Answers

I am thinking there is a subscription in subscribeToWhatever that is not completed before the expect.

Try using fakeAsync/tick.

it('should check some stuff', fakeAsync(()=>{
         fixture.detectChanges();
         // add tick here
         tick();   
       
         expect(mockMyService.serviceProperty).toEqual('someWhateverValue'); *//this one says that null doesn't equal 'someWhateverValue'*
      }));

Your methodology looks ok to me. This is how you can debug the tests: https://testing-angular.com/debugging-tests/.

Related