1

Honored community...

I am facing the following problem, which feels simple, but I can't find a solution...

I am testing a Controller-Class in Angular. It is legacy code with tons of dependency and I just want to write a unit test without mocking all the dependencies.

I am looking for a solution to mock nested functions, i.e. I call a method, and expect that the nested method was successfully called, without actually calling it.

A sample...

my-controller.ts

export class MyController {

   constructor () {}
   public init() {
      /* doStuff */
      this.initializeMyObject();
   }

   private initializeMyObject(): void {
     /*  doOtherStuff  */
   }
}

my-controller.spec.ts

describe('MyController', () => {
  let controller: MyController;

  configureTestSuite(() => {
    TestBed.configureTestingModule({
      schemas: [NO_ERRORS_SCHEMA],
      imports: [/*.. module imports ..*/],
      providers: [ MyController ],
    });
  });

  beforeEach(() => {
    controller = TestBed.get(MyController);
  });

  it('completion should be called', () => {
    // arrange
    spyOn(controller, 'initializeMyObject');

    // act
    controller.init();

    // assert
    expect(controller.initializeMyObject).toHaveBeenCalled();
  });

});

I know that we usually do not mock methods of the Class being tested, but I know from other programming languages like Java that it works, and I am just curious how to do it (in addition to the fact, that mocking all nested dependcies would be a total overhead...).

I've read something about jasmine.createSpyObj but I doubt that the solution will be something like jasmine.createSpyObj('this', ['initializeMyObject']), please correct me if I am wrong.

(This is not part of the answer I am looking for, but if there exist some third party libs, I will be glad to have a recommendation and follow up.)

Help will be appreciated. Cheers.

elyahu
  • 165
  • 3
  • 14

1 Answers1

0

There are different ways to mock individual class methods with Jasmine. Here are a few examples.

When a method has no return value (void return type).

spyOn(object, 'method-name').and.callFake(() => null); 

When the method should return a given value.

spyOn(object, 'method-name').and.returnValue(value); 

When the method accepts parameter and should return a computed value based on them.

spyOn(object, 'method-name').and.callFake((p1, p2) => {
    // compute value
    return value;
}); 
uminder
  • 23,831
  • 5
  • 37
  • 72
  • but how can I test if the method was really called? how to verify: ```expect(object.method-name).toHaveBeenCalled()```? – elyahu Nov 26 '19 at 13:58
  • `expect(object.method-name).toHaveBeenCalled()` is just fine – uminder Nov 26 '19 at 14:00
  • Can you please share the full content of the `MyController.init` and `MyController.initializeMyObject` methods? Maybe you perform some asynchronous work in there. – uminder Nov 26 '19 at 14:03
  • Please also consider this answer since you're spying on a private method: https://stackoverflow.com/a/48734437/2358409 – uminder Nov 26 '19 at 14:22
  • I do have an asychronos function in there, but this one is mocked. and I receive a console.log output a line before ```initializeMyObject``` is called, so it definitely gets there. ```spyOn``` or ```MyController.prototype``` are not successfull either – elyahu Nov 26 '19 at 14:31
  • Try to wrap your test with `fakeAsync`: https://angular.io/api/core/testing/fakeAsync – uminder Nov 26 '19 at 15:00
  • as mentioned above, I don't think that the problem is in the async function, since as mentioned it is stubbed.. the method directly resolves. and i am getting to the ```then``` case where my ```initializeMyController``` is – elyahu Nov 26 '19 at 15:14