You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

测试Angular组件时出现NG02200错误,寻求解决方法

Angular DashboardComponent单元测试错误排查与修复

问题背景

我正在做一个用于学习的Angular示例项目,组件从sessionStorage读取数据并展示,需要为DashboardComponent编写单元测试用例。默认的测试用例执行失败,尝试在Spec文件中手动创建existingUser数组后问题仍未解决。

相关代码

组件TS文件

export class DashboardComponent implements OnInit {
  existingUsers: User[] = [];

  constructor(
    private userservice: UserserviceService,
    private routerService: Router
  ) {}

  ngOnInit(): void {
    this.existingUsers = JSON.parse(
      sessionStorage.getItem('usersData') || '{}'
    );

    this.userservice.newUserSubject.subscribe((user: any[]) => {
      if (user.length >= 1) {
        this.existingUsers.push(user[user.length - 1]);
      }
    });

    sessionStorage.setItem('usersData', JSON.stringify(this.existingUsers));
  }
}

组件HTML文件

<div class="container responsive-grid">
  <mat-card *ngFor="let users of existingUsers; let index = index">
    <mat-card-header>
      <mat-card-title>{{ users.name }}</mat-card-title>
    </mat-card-header>
    <mat-card-actions>
      <button (click)="editUser(index, users)">Edit User</button>
      <button (click)="deleteUser(index)">Delete User</button>
    </mat-card-actions>
    <br />
    <mat-card-content>
      <br />
      <div>
        <strong><label for="name">Name</label></strong> {{ users.username }}<br />
        <strong><label for="username">Email</label></strong> {{ users.email }}<br />
        <div>
          <strong> Address</strong>
          <div>
            <strong><label for="street">Street</label></strong>
            {{ users.address.street }}<br />
          </div>
          <div>
            <strong><label for="suite">Suite</label></strong>
            {{ users.address.suite }}<br />
          </div>
          <div>
            <strong><label for="city">City</label> {{ users.address.city }}</strong><br />
          </div>
          <div>
            <strong><label for="zipcode">PinCode</label> </strong>{{ users.address.zipcode }}<br />
          </div>
        </div>
      </div>
    </mat-card-content>
  </mat-card>
</div>

初始测试Spec文件

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

错误信息

Error: NG02200: Cannot find a differ supporting object '[object Object]' of type 'object'.
NgFor only supports binding to Iterables, such as Arrays.
Did you mean to use the keyvalue pipe?

问题根源

组件初始化时,当sessionStorage中不存在usersData时,用'{}'作为默认解析值,JSON.parse后得到空对象,但existingUsers声明为数组类型。*ngFor指令只能遍历数组这类可迭代对象,因此抛出NG02200错误。即使在测试中手动设置existingUser数组,组件的ngOnInit方法也会覆盖这个值,导致问题持续存在。

解决方案

1. 修复组件核心逻辑

将默认解析值从'{}'改为'[]',确保无论sessionStorage是否有数据,existingUsers始终是数组类型:

export class DashboardComponent implements OnInit {
  existingUsers: User[] = [];

  constructor(
    private userservice: UserserviceService,
    private routerService: Router
  ) {}

  ngOnInit(): void {
    // 改为空数组作为默认值,避免解析出对象
    this.existingUsers = JSON.parse(
      sessionStorage.getItem('usersData') || '[]'
    );

    this.userservice.newUserSubject.subscribe((user: any[]) => {
      if (user.length >= 1) {
        this.existingUsers.push(user[user.length - 1]);
      }
    });

    sessionStorage.setItem('usersData', JSON.stringify(this.existingUsers));
  }
}

2. 编写正确的单元测试用例

模拟sessionStorage和用户服务的Subject,确保测试不依赖真实浏览器环境:

import { ComponentFixture, TestBed } from '@angular/core/testing';
import { DashboardComponent } from './dashboard.component';
import { UserserviceService } from '../userservice.service';
import { Router } from '@angular/router';
import { BehaviorSubject } from 'rxjs';

describe('DashboardComponent', () => {
  let component: DashboardComponent;
  let fixture: ComponentFixture<DashboardComponent>;
  let mockUserService: jasmine.SpyObj<UserserviceService>;
  let mockRouter: jasmine.SpyObj<Router>;
  let newUserSubject: BehaviorSubject<any[]>;

  beforeEach(async () => {
    // 创建模拟的Subject,用于测试订阅逻辑
    newUserSubject = new BehaviorSubject<any[]>([]);
    // 模拟用户服务
    mockUserService = jasmine.createSpyObj('UserserviceService', [], {
      newUserSubject: newUserSubject
    });
    // 模拟路由服务
    mockRouter = jasmine.createSpyObj('Router', ['navigate']);

    await TestBed.configureTestingModule({
      declarations: [DashboardComponent],
      providers: [
        { provide: UserserviceService, useValue: mockUserService },
        { provide: Router, useValue: mockRouter }
      ]
    }).compileComponents();

    // 模拟sessionStorage的读写方法
    spyOn(sessionStorage, 'getItem').and.returnValue(JSON.stringify([]));
    spyOn(sessionStorage, 'setItem');
  });

  beforeEach(() => {
    fixture = TestBed.createComponent(DashboardComponent);
    component = fixture.componentInstance;
    fixture.detectChanges(); // 触发组件初始化
  });

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

  it('should initialize existingUsers from sessionStorage', () => {
    expect(sessionStorage.getItem).toHaveBeenCalledWith('usersData');
    expect(component.existingUsers).toEqual([]);
    expect(sessionStorage.setItem).toHaveBeenCalledWith('usersData', JSON.stringify([]));
  });

  it('should add new user when newUserSubject emits data', () => {
    const testUser = { 
      name: 'Test User', 
      username: 'test', 
      email: 'test@example.com', 
      address: { street: 'Test St', suite: '101', city: 'Test City', zipcode: '12345' } 
    };
    // 让Subject发射测试数据
    newUserSubject.next([testUser]);
    // 验证用户被添加到数组中
    expect(component.existingUsers.length).toBe(1);
    expect(component.existingUsers[0]).toEqual(testUser);
  });
});

内容的提问来源于stack exchange,提问作者sejal purohit

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 01:22:05