角测试间谍没有被召唤

时间:2019-01-25 17:42:57

标签: angular typescript unit-testing mocking karma-jasmine

我无法配置Angular测试以使其正常工作。具体的问题是我的间谍似乎无法正常工作。我是Angular的新手,试图了解如何编写测试。背景是这是我的第一个Angular应用程序(使用CLI的最新版本7.x)。这是一个简单的幻灯片。幻灯片效果很好,但我只是想使测试生效。

第一部分是应用程序需要从JavaScript window.location获取href。根据有关Stackoverflow和其他地方的建议,我创建了一个包装窗口的类,以便可以对其进行测试。这是在名为windowobject.service.ts的服务中看起来像的东西:

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

function getWindow (): any {
  return window;
}

@Injectable({
  providedIn: 'root'
})
export class WindowObjectService {

  constructor() { }

  get browserWindow(): any {
    return getWindow();
  }
}

这可以正常工作,但问题是在测试中对其进行了嘲笑。我的测试称为photos.component.spec.ts:

import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { PhotosComponent } from './photos.component';
import { NgbModule } from '@ng-bootstrap/ng-bootstrap' ;
import { WindowObjectService } from '../services/windowobject.service';
import { environment } from '../../environments/environment';

const expectedId = 916;

class MockWindowObjectService {
  browserWindow(): any {
    return { window: {
                        location: {
                            href: environment.baseUrl + '/angular/slideshow/index.html?id=' + expectedId
                        }
                      }
    };
  }
}

describe('PhotosComponent', () => {
  let component: PhotosComponent;
  let fixture: ComponentFixture<PhotosComponent>;
  let windowService: MockWindowObjectService;
  let windowSpy;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      imports: [ HttpClientTestingModule, NgbModule ],
      declarations: [PhotosComponent],
      providers: [ { provide: WindowObjectService, useClass: MockWindowObjectService } ]
    })
    .compileComponents().then(() => {

      windowService = TestBed.get(WindowObjectService);
      fixture = TestBed.createComponent(PhotosComponent);
      component = fixture.componentInstance;
    });
  }));

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

  it('should call window object service', () => {
    windowSpy = spyOn(windowService, 'browserWindow').and.callThrough();
    expect(windowService.browserWindow).toHaveBeenCalled();
  });

});

我在这里想要做的是模拟窗口对象服务并确定它已被调用。确认已成功调用它后,我要编写的下一个测试是确认它正在返回模拟值。我已经尝试了此测试的许多不同配置,这只是最新的迭代,但是似乎没有任何效果。我只有一个错误,这与间谍没有得到召唤有关。有什么想法可以解决此问题并使模拟和间谍工作吗?

更新,添加photos.component.ts。它使用ng-bootstrap轮播和分页显示幻灯片演示,一次显示一张图像,并且可以使用轮播箭头或分页组件进行导航。图片集ID来自网址中的查询字符串值。

import { Component, OnInit, ViewChild } from '@angular/core';
import { PhotosService } from '../services/photos.service';
import { IPhoto } from '../models/photo';
import { NgbCarousel, NgbCarouselConfig, NgbPaginationConfig  } from '@ng-bootstrap/ng-bootstrap';
import { WindowObjectService } from '../services/windowobject.service';

@Component({
  selector: 'app-photos',
  templateUrl: './photos.component.html',
  styleUrls: ['./photos.component.scss'],
  providers: [NgbCarouselConfig, WindowObjectService]
})
export class PhotosComponent implements OnInit {

  // reference to "photosCarousel"
  @ViewChild('photosCarousel') photosCarousel: NgbCarousel;

  private _photosService: any;
  private _windowService: WindowObjectService;

  errorMessage: string;
  photos: IPhoto[] = new Array;
  page = 1;
  collectionSize: number;
  tripReportId: string;

  constructor(carouselConfig: NgbCarouselConfig, paginationConfig: NgbPaginationConfig, phototsService: PhotosService,
              windowService: WindowObjectService) {

    carouselConfig.showNavigationArrows = true;
    carouselConfig.showNavigationIndicators = false;
    carouselConfig.interval = 0; // Amount of time in milliseconds before next slide is shown.
    carouselConfig.wrap = false;

    paginationConfig.pageSize = 1;
    paginationConfig.maxSize = 5;
    paginationConfig.size = 'sm';
    paginationConfig.boundaryLinks = false;

    this._photosService = phototsService;
    this._windowService = windowService;
  }

  ngOnInit() {

    console.log('this._windowService.browserWindow.location.href', this._windowService.browserWindow.location.href);

    this.tripReportId = this._windowService.browserWindow.location.href.split('?')[1].split('=')[1];

    this._photosService.getPhotos(this.tripReportId).subscribe(
      photos => {
        this.photos = photos;
        this.collectionSize = photos.length;
      },
      error => this.errorMessage = <any>error
    );

    this.collectionSize = this.photos.length;
  }

  pageChanged(pageNumber: number): void {

    this.photosCarousel.select(pageNumber.toString());
  }

  public onSlide(slideData) {
    this.page = slideData.current;
  }
}

1 个答案:

答案 0 :(得分:0)

好的,这是我将您的代码放入此StackBlitz中时发现的。

  • 我怀疑,您需要调用fixture.detectChanges()才能执行ngOnInit()。
  • 由于在@Component装饰器的providers数组中声明了WindowObjectService,因此无法在默认provider数组中用模拟代替。而是需要使用overrideComponent(),如StackBlitz中所示。
  • 您的组件将windowService设置为构造函数中的组件变量。因此,使用类进行模拟非常困难,因此我将模拟更改为对象。
  • 同样,因为您在构造函数中设置了变量,所以必须在创建组件之前非常早地完成spyOnProperty()。
  • 如果您直接监视windowService.browserWindow会导致问题,所以请使用设置的spy变量。
  • 我还展示了如何对PhotosService进行一些数据模拟,以使您也从此开始。

如您所见,测试现在通过了。我希望这会有所帮助。