getsaf / shallow-render

Angular testing made easy with shallow rendering and easy mocking. https://getsaf.github.io/shallow-render
MIT License
273 stars 25 forks source link

Mocking a single function #191

Closed azaeng04 closed 3 years ago

azaeng04 commented 3 years ago

Hi I am trying to mock a single function in a service instead of the entire service and their functions. One function I want to mock and the other I want to use the actual implementation of. Also both functions reside in the same service.

How do I accomplish?

getsaf commented 3 years ago

If you want to keep the entire service except for one function I would generally do this by using dontMock and injecting the service directly into the test, then applying a single spy:

  const {inject} = await shallow.dontMock(MyService).render();
  const realServiceInstance = inject(MyService);
  jest.spyOn(realServiceInstance, 'someAsyncFunction').mockResolvedValue(123);

Here it is in a full example:

it('can get real instances of a service', async () => {
  const {inject, fixture} = await shallow
    .dontMock(MyService)
    .render({
      // Optional arguments if the service is used in an onInit, you can delay onInit
      // by skipping detectChanges and whenStable checks on render
      detectChanges: false,
      whenStable: false
    });

  const realServiceInstance = inject(MyService);
  jest.spyOn(realServiceInstance, 'someAsyncFunction').mockResolvedValue(123);

  // This fires onInit after you've mocked your service.
  fixture.detectChanges();
  await fixture.whenStable();
  //...
});
azaeng04 commented 3 years ago

I just tried that and I seem to get an error thrown:

beforeEach( async () => {
      shallowComponent = new Shallow(LoginComponent, LoginModule)
      .replaceModule(RouterModule, RouterTestingModule)
      .replaceModule(HttpClientModule, HttpClientTestingModule)
      .dontMock(ThemeService)
      .mock(VossApiService, { getLogin: () => of(loginData) })
      .mock(Store, {})
      .mock(ConfirmationService, {})
      .mock(AuthenticationService, { clearSessionMonitor: () => null });
    });

    it('shows the input', async () => {
      const { inject, find } = await shallowComponent
      .render({
        detectChanges: false,
        whenStable: false
      });
      themeService = inject(ThemeService);
      spyOn(themeService, 'getThemeByName')

NullInjectorError: R3InjectorError(DynamicTestModule)[ThemeService -> ThemeService]:   NullInjectorError: No provider for ThemeService! 

getsaf commented 3 years ago

My best guess here is that your Login module does not provide the ThemeService. You can provide it manually in this test with .provide or .provideMock. If this is a top-level service that you need for all your tests, you can use Shallow.alwaysProvide in your global test setup so you only have to do it once.

azaeng04 commented 3 years ago

Thanks so much you are right! That helped.