预期的间谍“服务方法”已被调用。 - 角 10

Posted

技术标签:

【中文标题】预期的间谍“服务方法”已被调用。 - 角 10【英文标题】:Expected spy "service method" to have been called. - Angular 10 【发布时间】:2021-02-04 19:02:45 【问题描述】:

我正在用 Angular 编写一个组件单元测试用例,但我不确定我在哪里做错了。早些时候,我写过类似的测试用例,但这个让我头疼。

请检查这里的代码:

组件代码:

ngOnInit(): void 
    this.callFavoriteProjects();
  

  /**
   * @description To fetch all the favorites projects
   */
  public callFavoriteProjects() 
    this.loadingService.show('loading-favorites');
    this.subscriptions[this.subscriptions.length] = this.projectsFavoriteService.getAllUserFavoriteProjects()
      .subscribe((data) => 
        this.favoriteProjectsList = data;
        this.filteredProjectList = data;
        this.loadingService.hide('loading-favorites');
      ,
        (error) => 
          this.toastrService.error('', 'There is an error while processing the favorite projects!', 
            timeOut: 3000,
            progressBar: true,
            progressAnimation: 'increasing',
          );
          this.loadingService.hide('loading-favorites');
        
      );
  

Jasmine 测试用例代码:

describe('FavoriteProjectsComponent', () => 
  let component: FavoriteProjectsComponent;
  let fixture: ComponentFixture<FavoriteProjectsComponent>;
  let projectsFavoriteService: ProjectsFavoriteService;

  beforeEach(async(() => 
    TestBed.configureTestingModule(
      imports: [
        HttpClientTestingModule,
        RouterTestingModule,
        MaterialModule,
        ToastrModule.forRoot()
      ],
      declarations: [ FavoriteProjectsComponent ],
      schemas: [CUSTOM_ELEMENTS_SCHEMA],
      providers: [
         provide: NgxSpinnerService, useClass: NgxSpinnerServiceStub ,
         provide: ToastrService, useClass: ToastrServiceStub ,
         provide: ProjectsHelperService, useClass: ProjectsHelperServiceStub ,
         provide: ProjectsFavoriteService, useClass: ProjectsFavoriteServiceStub 
      ]
    )
    .compileComponents();
  ));

  beforeEach(() => 
    projectsFavoriteService = TestBed.inject(ProjectsFavoriteService);
  );

  beforeEach(() => 
    fixture = TestBed.createComponent(FavoriteProjectsComponent);
    component = fixture.componentInstance;
    // fixture.detectChanges();
  );

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


  describe('method: ngOnInit should ', () => 
    it('call callFavoriteProjects, projectsFavoriteService.getAllUserFavoriteProjects() and return an Observable<any>', () => 
      spyOn(projectsFavoriteService, 'getAllUserFavoriteProjects').and.returnValue(observableOf([]));
      spyOn(component, 'callFavoriteProjects');
      component.ngOnInit();
      expect(component.callFavoriteProjects).toHaveBeenCalled();
      expect(projectsFavoriteService.getAllUserFavoriteProjects).toHaveBeenCalled();
    );

    it('call callFavoriteProjects, projectsFavoriteService.getAllUserFavoriteProjects() but throw Observable error', () => 
      spyOn(projectsFavoriteService, 'getAllUserFavoriteProjects').and.returnValue(throwError( message: 'Error Message' ));
      spyOn(component, 'callFavoriteProjects');
      component.ngOnInit();
      expect(component.callFavoriteProjects).toHaveBeenCalled();
      expect(projectsFavoriteService.getAllUserFavoriteProjects).toHaveBeenCalled();
    );

  );

);

项目收藏存根文件

import  of as observableOf  from 'rxjs';

export class ProjectsFavoriteServiceStub 

  getAllUserFavoriteProjects() 
    return observableOf([]);
  
    

所以,如果 ngOnInit 失败,两个测试用例都会失败。我也尝试了 fixture.detectChanges(),方法是在调用 ngOnInit() 之后将其放入 beforeEach plus 中。

我没有找到错误的方向。请帮忙。提前致谢。

【问题讨论】:

【参考方案1】:

问题是spyOn(component, 'callFavoriteProjects')。当您这样做时,我们会丢失有关 callFavoriteProjects 的实现细节,其中该方法/函数将返回 undefined 并且我们只知道它是否被调用。我们必须写.and.callTrough 来实际调用这个函数,而不是仅仅监视它是否被调用。

我已经注释了需要更改的行。

describe('method: ngOnInit should ', () => 
    it('call callFavoriteProjects, projectsFavoriteService.getAllUserFavoriteProjects() and return an Observable<any>', () => 
      spyOn(projectsFavoriteService, 'getAllUserFavoriteProjects').and.returnValue(observableOf([]));
      spyOn(component, 'callFavoriteProjects').and.callThrough(); // change this line
      component.ngOnInit();
      expect(component.callFavoriteProjects).toHaveBeenCalled();
      expect(projectsFavoriteService.getAllUserFavoriteProjects).toHaveBeenCalled();
    );

    it('call callFavoriteProjects, projectsFavoriteService.getAllUserFavoriteProjects() but throw Observable error', () => 
      spyOn(projectsFavoriteService, 'getAllUserFavoriteProjects').and.returnValue(throwError( message: 'Error Message' ));
      spyOn(component, 'callFavoriteProjects').and.callThrough(); // change this line
      component.ngOnInit();
      expect(component.callFavoriteProjects).toHaveBeenCalled();
      expect(projectsFavoriteService.getAllUserFavoriteProjects).toHaveBeenCalled();
    );

  );

【讨论】:

它就像一个魅力。很高兴知道 .callThrough() 有这么大的力量。我曾经这样做,但它似乎适用于旧版本的 Karma Jasmine。我目前正在使用最新的。

以上是关于预期的间谍“服务方法”已被调用。 - 角 10的主要内容,如果未能解决你的问题,请参考以下文章

期待一个间谍,但得到了 BehaviorSubject

如何为 Jasmine 间谍的多个调用提供不同的返回值

角色与间谍的间谍

洛谷 P1262 间谍网络==Codevs 4093 EZ的间谍网络

Mockito:将InOrder与间谍对象一起使用

间谍在componentDidMount中的方法调用