DEV Community

Cover image for [Angular] Testing Native Browser Objects
Tato Petry
Tato Petry

Posted on

[Angular] Testing Native Browser Objects

The Problem

Imagine you're building an Angular application and need to redirect the user to an external site, like the website of the American grunge band Pearl Jam. A common way to do this is by manipulating the window object. For example, you might have a function that changes the window.location.href to redirect the user:

redirectToPearlJam() {
  window.location.href = 'https://pearljam.com';
}
Enter fullscreen mode Exit fullscreen mode

Now, how do we test this?

Testing the Redirect Function

Testing code that manipulates the window object can be tricky since window is a global browser API. But don't worry, there are solutions! Here are some tips to do this in a straightforward way.

1. Using Jasmine Spy

The first solution is to use Jasmine spies to monitor and intercept calls to window.location.href. Here’s how to do it:

import { ComponentFixture, TestBed } from '@angular/core/testing';
import { YourComponent } from './your-component';

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

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

    fixture = TestBed.createComponent(YourComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('should redirect to Pearl Jam website', () => {
    spyOn(window, 'open');
    component.redirectToPearlJam();
    expect(window.open).toHaveBeenCalledWith('https://pearljam.com');
  });
});
Enter fullscreen mode Exit fullscreen mode

2. Mocking Window Object

Another way is to create a mock of the window object to use in tests. We can do this by injecting window into the component:

export function windowFactory() {
  return window;
}

@NgModule({
  providers: [
    { provide: 'Window', useFactory: windowFactory }
  ]
})
export class AppModule { }
Enter fullscreen mode Exit fullscreen mode

And in the component, we use the injected object:

import { Inject, Injectable } from '@angular/core';

@Injectable({
  providedIn: 'root',
})
export class YourService {
  constructor(@Inject('Window') private window: Window) {}

  redirectToPearlJam() {
    this.window.location.href = 'https://pearljam.com';
  }
}
Enter fullscreen mode Exit fullscreen mode

In the test, we can create a mock of window:

import { TestBed } from '@angular/core/testing';
import { YourService } from './your-service';

describe('YourService', () => {
  let service: YourService;
  let mockWindow: { location: { href: string } };

  beforeEach(() => {
    mockWindow = { location: { href: '' } };

    TestBed.configureTestingModule({
      providers: [
        YourService,
        { provide: 'Window', useValue: mockWindow }
      ]
    });

    service = TestBed.inject(YourService);
  });

  it('should redirect to Pearl Jam website', () => {
    service.redirectToPearlJam();
    expect(mockWindow.location.href).toEqual('https://pearljam.com');
  });
});
Enter fullscreen mode Exit fullscreen mode

3. Assigning Mock to Component Variable

Another approach is to create a variable inside the component that holds the window object. In the tests, we can assign a mock to this variable. Here's how to do it:

In the component, add a variable for window:

export class YourComponent {
  private window: Window;

  constructor() {
    this.window = window;
  }

  redirectToPearlJam() {
    this.window.location.href = 'https://pearljam.com';
  }
}
Enter fullscreen mode Exit fullscreen mode

In the test, we replace the component’s window variable with a mock:

import { ComponentFixture, TestBed } from '@angular/core/testing';
import { YourComponent } from './your-component';

describe('YourComponent', () => {
  let component: YourComponent;
  let fixture: ComponentFixture<YourComponent>;
  let mockWindow: { location: { href: string } };

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

    fixture = TestBed.createComponent(YourComponent);
    component = fixture.componentInstance;
    mockWindow = { location: { href: '' } };
    component['window'] = mockWindow;  // Assign the mock here
    fixture.detectChanges();
  });

  it('should redirect to Pearl Jam website', () => {
    component.redirectToPearlJam();
    expect(mockWindow.location.href).toEqual('https://pearljam.com');
  });
});
Enter fullscreen mode Exit fullscreen mode

Conclusion

Testing code that interacts with the window object in Angular might seem complicated, but with these strategies, it becomes much easier. Using Jasmine spies, creating a mock of window, or using a component variable for the window object are effective approaches that will help you keep your tests clean and functional.

Top comments (0)