0

I have a function to be unit tested that I'm not sure how to approach. Simplified:

someFunction(): boolean {
 this.service.login().subscribe(response => {
  if (response) {
    return someOtherFunction();
  }
 });
}

someOtherFunction(): boolean {
 this.service.otherTask().subscribe(response => {
  if (response) {
    return true;
  }
 });
}

I want to test the result of someFunction in this case. However, this does not work:

describe('someFunction', () => {
  it('returns true', () => {
   serviceSpy.login.and.returnValue(of({response: response}));
   serviceSpy.otherTask.and.returnValue(of({response: otherResponse}));
   result = component.someFunction();
   expect(result).toEqual(true);
 });
});

ServiceSpy has been configured before this block. I can see that the functions are executed and that true is returned. However, at the moment I am asking for result, it is still undefined. The test framework does not wait for everything to be completed. I have tried to use async, fakeAsync, done(), but these do not do the trick. Is there a way to test the return value of someFunction?

satanTime
  • 12,631
  • 1
  • 25
  • 73
Century
  • 283
  • 1
  • 4
  • 22
  • 1
    someFunction doesn't *have* a return value. Nor does someOtherFunction. I'm surprised the TypeScript compiler isn't yelling at you. – jonrsharpe May 07 '20 at 07:22
  • I'm surprised it works at all. It does seem to do what it needs to do... Does it matter that someFunction is the canActivate of the authentication-guard? – Century May 07 '20 at 07:39
  • @Century : Its a bad code. please refactor it – Shashank Vivek May 07 '20 at 08:27
  • I can't imagine it does what it needs to do as canActivate. It returns undefined, which is false-y, so the route will *never* get activated. – jonrsharpe May 07 '20 at 08:31
  • Fortunately, most of the traffic follows a different path without a subscription. Still, this part seemed to work somehow... my compiler did not warn me either. Anyway, I made the someFunction return an Observable (which is luckily fine for canActivate) and return the login but use a pipe and map there. Should I post it as an answer? – Century May 07 '20 at 13:07

2 Answers2

2

The problem is within the functions, they returns result inside of subscribe, that won't work, you need to return an observable or to use a local variables.

someFunction(): Observable<boolean> {
 return this.service.login().pipe(
   first(),
   switchMap(res => res ? this.someOtherFunction() : of(undefined)),
 );
}

someOtherFunction(): Observable<boolean> {
 return this.service.otherTask().pipe(
   first(),
   map(response => !!response),
 );
}

and then in your test you can do

describe('someFunction', (done) => {
  it('returns true', () => {
   serviceSpy.login.and.returnValue(of({response: response}));
   serviceSpy.otherTask.and.returnValue(of({response: otherResponse}));
   component.someFunction().subscribe(result => {
     expect(result).toEqual(true);
     done();
   });
 });
});
satanTime
  • 12,631
  • 1
  • 25
  • 73
1

With inspiration from the answer of @satanTime and the comments and some more internet searching, I edited my code and test as follows. The someOtherFunction appeared not to have a subscription that was in any way an issue, so I omitted it in the simplification.

someFunction(): Observable<boolean> {
 return this.service.login().pipe(map(response => {
  if (response) {
    return someOtherFunction();
  }
 }));
}

someOtherFunction(): boolean {
    return true;
}

Test:

describe('someFunction', () => {
  it('returns true', (done) => {
   serviceSpy.login.and.returnValue(of({response: response}));
   serviceSpy.otherTask.and.returnValue(of({response: otherResponse}));
   component.someFunction().subscribe(result => {
   expect(result).toEqual(true);
   done();
   });
 });
});
Century
  • 283
  • 1
  • 4
  • 22