简体   繁体   中英

Angular CLI Unit Testing An Extended Window Method

I am trying to write a unit test for an Angular CLI (v6.0.8, with @angular/core v6.0.9) component that calls a method on the window object injected via a service, but I'm running into some issues.

Here is the component: MyComponent

import { WindowRefService} from '../../services/window-reference/window-reference.service'

export class MyComponent {

  constructor(private windowRef: WindowRefService) {}

  addAnother() {
    if ((this.windowRef.nativeWindow._thirdPartyWindowObject) {
      this.windowRef.nativeWindow._thirdPartyWindowObject.track("toolstart")
    }
  }
}

and here is the service: WindowRefService

import { Injectable } from '@angular/core'

export interface ICustomWindow extends Window {
  _thirdPartyWindowObject: {
    track: Function
  }
}

function getWindow (): any {
    return window
}

@Injectable()
export class WindowRefService {
    get nativeWindow (): ICustomWindow {
        return getWindow()
    }
}

This service is a way to extend the Window type to include a third party library introduced into the window object in the page the app is loaded into. Not ideal, I know, but it's the use case I was handed.

This is the test I've written for the addAnother method:

import { async, ComponentFixture, TestBed } from '@angular/core/testing'

import { MyComponent } from './MyComponent'
import { WindowRefService, ICustomWindow } from '../../services/window-reference/window-reference.service'

class WindowServiceMock {
  get nativeWindow (): ICustomWindow {
    return {
      ...window,
      _thirdPartyWindowObject: {
        track: (props: string) => {
          console.log('props', props)
          return true
        }
      }
    }
  }
}

describe('MyComponent', () => {
  let component: MyComponent
  let fixture: ComponentFixture<MyComponent>
  let windowSpy: WindowServiceMock

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [ MyComponent ],
    })
    .overrideComponent(MyComponent, {
      set: {
        providers: [
          { provide: WindowRefService, useClass: WindowServiceMock }
        ]
      }
    })
    .compileComponents()
  }))

  beforeEach(() => {
    fixture = TestBed.createComponent(MyComponent)
    component = fixture.componentInstance
    windowSpy = fixture.debugElement.injector.get(WindowRefService) as any
    fixture.detectChanges()
  })

  it('should call the third party scripts', () => {
    let spy = spyOn(windowSpy.nativeWindow._thirdPartyWindowObject, 'track')
    component.addAnother()
    expect(spy).toHaveBeenCalled()
  })
})

This all works perfectly up until the point I would expect the spy for windowSpy.nativeWindow._thirdPartyWindowObject.track to have been called. When I check my logs, I can see the toolstart string being passed into the track method inside WindowServiceMock being logged out so the stubbed service is being called. However, for some reason, the spy is not.

I'm sure I'm missing something extremely obvious here, but any help would be appreciated.

Thanks!

The issue is that the nativeWindow getter is returning a new object every time it is accessed. So the function you spied on is not the same one that then gets called later. Updating the mock's getter to always return a reference to the same _thirdPartyWindowObject object, and hence the same child function, as shown below should fix the issue. Not sure if there is a better the jasmine way to do this though.

const _thirdPartyWindowObject = {
  track: (props: string) => {
    console.log('props', props)
    return true
  }
}

class WindowServiceMock {
  get nativeWindow (): ICustomWindow {
    return {
      ...window,
      _thirdPartyWindowObject
    }
  }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM