在单元测试中出现错误:无法读取未定义的属性(读取“订阅”)

Getting Error Cannot read properties of undefined (reading 'subscribe') in Unit testing

提问人:priyansh kasera 提问时间:11/7/2023 更新时间:11/7/2023 访问量:26

问:

我正在尝试用 angular 为我的组件编写一个单元测试,但是当它调用 Api 时,它会从响应中取消定义,我不知道如何处理这个问题,因为这是我第一次用 angular 编写单元测试,请帮忙!

这是我对组件 UserProfile 的单元测试的代码

import { ComponentFixture, TestBed,async } from '@angular/core/testing';
import { ApiService } from 'src/app/services/api.service';
import { UserProfileComponent } from './user-profile.component';

describe('UserProfileComponent', () => {
  let component: UserProfileComponent;
  let fixture: ComponentFixture<UserProfileComponent>;
  let apiService: jasmine.SpyObj<ApiService>;

  beforeEach(() => {
    apiService = jasmine.createSpyObj('ApiService', ['getUser', 'getRepo']);

    TestBed.configureTestingModule({
      declarations: [UserProfileComponent],
      providers: [{ provide: ApiService, useValue: apiService }],
    });

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

    // Initialize test data
    component.userDetail = { public_repos: 20 };
    component.userName = 'testuser';

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

这是我的 UserProfile 组件代码

import { Component, Input, OnInit } from '@angular/core';
import { ApiService } from 'src/app/services/api.service';

@Component({
  selector: 'app-user-profile',
  templateUrl: './user-profile.component.html',
  styleUrls: ['./user-profile.component.scss'],
})
export class UserProfileComponent implements OnInit {
  @Input() userDetail: any;
  @Input() userName: any;
  perPage: number;
  pageNo: number;
  isLoading: boolean;
  repoList: any;
  noOfPages: any[];
  options: { value: number; label: number }[] = [];

  constructor(private apiService: ApiService) {
    this.perPage = 10;
    this.pageNo = 1;
    this.isLoading = false;
    this.noOfPages = new Array(10);
    for (let i = 10; i <= 100; i++) {
      const tempObj = {
        value: i,
        label: i,
      };
      this.options.push(tempObj);
    }
  }

  getUserRepos() {
    this.isLoading = true;
    this.apiService.getRepo(this.userName, this.perPage, this.pageNo).subscribe(
      (data) => {
        this.repoList = data;
        setTimeout(() => {
          this.isLoading = false;
        }, 200);
      },
      (error) => {
        error.log(error);
        setTimeout(() => {
          this.isLoading = false;
        }, 200);
      }
    );
  }
  changePageNo(pageNo: number) {
    this.pageNo =
      pageNo <= 0
        ? 1
        : pageNo > this.noOfPages.length
        ? this.noOfPages.length
        : pageNo;
    this.getUserRepos();
  }
  onPerPageNoChange() {
    this.noOfPages = new Array(
      Math.floor(this.userDetail.public_repos / this.perPage) + 1
    );
    this.pageNo = 1;
    this.getUserRepos();
  }
  ngOnInit() {
    this.noOfPages = new Array(
      Math.floor(this.userDetail.public_repos / this.perPage) + 1
    );
    console.log(this.noOfPages)
    this.getUserRepos();
  }
}

服务中 getRepo 方法的代码

getRepo(githubUsername : string,perPage : number,pageNo : number){
    return this.httpClient.get(`https://api.github.com/users/${githubUsername}/repos?per_page=${perPage}&page=${pageNo}`)
  }
Angular 单元 Karma-Jasmine 测试平台

评论


答:

0赞 Philip 11/7/2023 #1

这样做的原因是,您曾经创建一个对象,该对象将同时包含 a 和 a 方法的间谍。默认情况下,它们不会返回任何内容,但 .jasmine.createSpyObjgetUsergetRepoundefined

您可以通过执行以下操作来更改此设置:

  beforeEach(() => {
    apiService = jasmine.createSpyObj('ApiService', ['getUser', 'getRepo']);
    apiService.getRepo.and.returnValue(of({...})); // <--- data that should be returned by your api goes here

    TestBed.configureTestingModule({
      declarations: [UserProfileComponent],
      providers: [{ provide: ApiService, useValue: apiService }],
    });

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

    // Initialize test data
    component.userDetail = { public_repos: 20 };
    component.userName = 'testuser';

    fixture.detectChanges();
  });

您还可以在每个单独的测试中放置行,以根据每个测试场景的需要更改您的行为。apiService.getRepo.and.returnValue(...)ApiService