How to unit test window.location.reload in angular 12 unit testing?

Viewed 3695

In my angular project, I have a function

reloadPage(): void {
  window.location.reload();
}

So when ever I need to reload the page, I used to call this function.

Now I am trying to add the unit testing for this function but it is not working.

it("reloadPage: should be validated", () => {
    spyOn(window.location, 'reload').and.callFake(() => {
      //test
    });
    component.reloadPage();
});

It still reloads the page on unit testing

How can I achieve a unit test for this function? Any help could be appreciated. Thanks in advance

3 Answers

You have to provide mock Window object in your TestBed.configureTestingModule

First create a window token

import { InjectionToken } from '@angular/core';

export const WINDOW = new InjectionToken('Window');

Create a mock object

windowMock = {
location: {
reload: jasmine.createSpy('reload')
 }
}

And then in providers

{provide: WINDOW, useValue: windowMock}

I found the following solution to work.

In your component, inject the window with an InjectionToken.

const MY_WINDOW_TOKEN = new InjectionToken<Window>('Window');

The constructor of your component then takes:

constructor(@Inject(MY_WINDOW_TOKEN) private window: Window) { ... }

In your module provide:

{ provide: MY_WINDOW_TOKEN, useValue: window }

In your test (spec) instead inject another (mock) window, by providing the mock / spy implementation in the TestBed configuration:

{ 
  provide: MY_WINDOW_TOKEN, 
  useFactory: () => {
    return jasmine.createSpyObj('Window', [...]);
  }
}

It basically comes down to isolating your subject of test from it's dependencies.

(Notice: all code is written from memory, and may need some modifications to actually work / compile)

There's no easy way to test if location.reload() has been called or not. It's not worth the headache either (also you should not refresh the page, this is a hack, and it's valuable on very rare occasions, like if you are playing with environment stuff on runtime)...

So to be able to test any logic for onEvent function instead (which is needed), and to skip any error derived by refreshing the page (which we are going to hide), you might have a component like this:

@Component({
  ...
})
export class SomethingComponent {
  ...

  onEvent = () => {
    ... some logic to test
    this.reloadPage();
  }

  private reloadPage = () => location.reload();
}

then the test will looking like this:

describe('SomethingComponent', () => {
  let component: SomethingComponent;
  let fixture: ComponentFixture<SomethingComponent>;

  beforeEach(async () => {
    await TestBed.configureTestingModule({
      declarations: [ SomethingComponent ]
    })
      .compileComponents();
  });

  beforeEach(() => {
    fixture = TestBed.createComponent(SomethingComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('should do something onEvent', () => {
    // hack to change a private attribute of your component
    // strongly suggest not to use this as a routine
    component[ 'reloadPage' ] = () => null;

    component.onEvent();

    ... some tests here ...

    // do not test expect(location.reload).toHasBeenCalled()
    // it won't work and it's not worth make this working
  });
});

This will avoid the error on the refresh page and you'll be able to actually check any logic in your onEvent function.

Related