Angular 4 使用 window 对代码进行单元测试

Angular 4 Unit Testing the code using window

我想测试一个代码

public openAttachment(attachment: Attachment) {
    if (window.navigator && window.navigator.msSaveOrOpenBlob) {
        window.navigator.msSaveOrOpenBlob(attachment.getFile());
    }
    else {
        let objectUrl = URL.createObjectURL(attachment.getFile());
        window.open(objectUrl);
    }
}

我不知道如何访问 window 或模拟 window 以对其进行测试。我是 angular 测试的新手,所以如果你能详细解释我就太好了!

您也可以在测试中访问 window 对象。所以你可以很容易地监视他们。

我已经根据您的特定用例创建了一个轻量级组件。

组件如下:

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

@Component({
  selector: 'app-attachment',
  templateUrl: './attachment.component.html',
  styleUrls: ['./attachment.component.css']
})
export class AttachmentComponent {

  public openAttachment(attachment) {
    if (window.navigator && window.navigator.msSaveOrOpenBlob) {
      window.navigator.msSaveOrOpenBlob(attachment.getFile());
    }
    else {
      let objectUrl = URL.createObjectURL(attachment.getFile());
      window.open(objectUrl);
    }
  }

}

请注意,这里我不确定 Attachment 类型是什么。所以我已经从 openAttachment 函数的参数中删除了该类型注释。

现在我的测试应该是这样的:

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

import { AttachmentComponent } from './attachment.component';

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

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

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

  it('should be created', () => {
    expect(component).toBeTruthy();
  });

  describe('openAttachment', () => {
    let attachment;
    beforeEach(() => {
      attachment = { getFile: function() { return 'foo'; } };
    });

    it('should call `window.open` if `msSaveOrOpenBlob` is not a method present on the `window.navigator`', () => {

      // Since this will probably run on Chrome, Chrome Headless or PhantomJS, if won't have a `msSaveOrOpenBlob` method on it.
      // So this is the test for the else condition.
      let windowOpenSpy = spyOn(window, 'open');
      let returnValue = { foo: 'bar' };
      let urlCreateObjectSpy = spyOn(URL, 'createObjectURL').and.returnValue(returnValue);

      component.openAttachment(attachment);

      expect(urlCreateObjectSpy).toHaveBeenCalledWith('foo');
      expect(windowOpenSpy).toHaveBeenCalledWith(returnValue);

    });

    it('should call the `window.navigator.msSaveOrOpenBlob` if `msSaveOrOpenBlob` is present on the navigator object', () => {

      // To cover the if condition, we'll have to attach a `msSaveOrOpenBlob` method on the window.navigator object.
      // We can then spy on it and check whether that spy was called or not.
      // Our implementation will have to return a boolean because that's what is the return type of `msSaveOrOpenBlob`.
      window.navigator.msSaveOrOpenBlob = function() { return true; };
      let msSaveOrOpenBlobSpy = spyOn(window.navigator, 'msSaveOrOpenBlob');

      component.openAttachment(attachment);

      expect(msSaveOrOpenBlobSpy).toHaveBeenCalledWith('foo');

    });

  });

});

再次强调,我不确定附件类型是什么。所以在我的 openAttachment describe 块的 beforeEach 块中,我将它分配给一个对象,该对象包含一个名为 getFile 的键,其值作为一个函数,最终将 return字符串 foo.

此外,由于默认情况下您的测试是 运行 in Chrome,因此您不会在 window.navigator 对象上获得 msSaveOrOpenBlob 函数。所以 openAttachment describe 块中的第一个测试只会覆盖 else 块。

虽然在第二个测试中,我们在 window.navigator 对象上添加了 msSaveOrOpenBlob 作为函数。所以现在它可以覆盖 if 分支。那么你可以在 msSaveOrOpenBlob 函数上创建一个间谍 expect 这个间谍 toHaveBeenCalledWith 无论是 return 从 attachment.getFile() 方法(字符串 foo 在这种情况下)

希望这对您有所帮助。

beforeEach(() => {

//使用下面的行

 (<any>window).navigator ={ function msSaveOrOpenBlob() {} };

   fixture = TestBed.createComponent(Your_Component);
   component = fixture.componentInstance;

   fixture.detectChanges();
}