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

Angular Karma/Jasmine单元测试报错:无法读取undefined的imageUrl属性

Angular单元测试报错排查:Cannot read properties of undefined reading imageUrl

问题背景

三个组件单元测试均失败,报错信息为Cannot read properties of undefined reading imageUrl,错误源自组件模板。尝试在TestBed中配置相关属性但无效,组件无法正常创建,目前已注释部分测试,附相关代码片段寻求排查方向与解决方案。

相关代码片段

模型文件 product.ts

export class Product {
  _id:string;
  name: string;
  size: string;
  description: string;
  price: number;
  imageUrl: string;
  static price: Product;

  constructor(_id:string, name: string, description="", size:string, price=0, imageUrl=""){
    this._id=_id;
    this.name = name;
    this.description = description;
    this.price= price;
    this.size= size;
    this.imageUrl = imageUrl;
   }
 }

组件模板 product-item.component.html

<img class=" img-fluid shacker"  [src]="productItem.imageUrl" alt="Clothing" />

测试文件 product-item.component.spec.ts

import { ComponentFixture, TestBed, async } from '@angular/core/testing';
import { WishlistItemService } from '@app/services/wishlist-item.service';
import { CartService } from '@app/services/cart.service';
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { ProductItemComponent } from './product-item.component';
import { RouterTestingModule } from '@angular/router/testing';
import { AlertComponent } from '@app/_components';
import { Product } from '@app/models/product';

const FAKE_PRODUCTS:Product[] = 
[
  {
    "_id": "0001",
    "name": "Black One Piece",
    "size": "M",
    "description": "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book.",
    "imageUrl": "http://localhost:4200/assets/BlacknwhiteOne.png",
    "price": 199
  },
  {
    "_id": "00002",
    "name": "Overalls",
    "size": "M",
    "description": "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book.",
    "imageUrl": "http://localhost:4200/assets/overalls.png",
    "price": 250
  }
]

const okResponse = new Response(JSON.stringify(FAKE_PRODUCTS), {
  status: 200,
  statusText: 'OK'
});

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

  beforeEach(async () => {
    const wishlistitemServiceSpy = jasmine.createSpyObj<WishlistItemService>(['addProductItemToWishlistItem']);
    const cartServiceSpy = jasmine.createSpyObj<CartService>(['addAllproducts']);

    await TestBed.configureTestingModule({
      declarations: [ ProductItemComponent, AlertComponent ],
      imports:[HttpClientTestingModule, RouterTestingModule],
      providers:[
        {
          WishlistItemService, useValue: wishlistitemServiceSpy,
          CartService, uses: cartServiceSpy
        }
      ]
    })
    .compileComponents();
  });

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

  it('should create', () => {
    component.imageUrl  //this is where I'm having trouble it needs to be mentioned not sure how  
    expect(component).toBeTruthy();
  });
});

组件文件 product-item.component.ts 片段

export class ProductItemComponent implements OnInit {
  [x: string]: any;

  form!: UntypedFormGroup; 
  submitted=false;
  sizeBy!: string;

  Valuesize!: string;
  size!: string;
  imageUrl!: string

  //products is my database collection
  //Products is my model/interface for db 
  products:Products[] = [];

  cartItems: CartItem[] = [];
  @Input() cartItem!: CartItem;

  @Input() productItem!: Product; 

  ngOnInit() {
    this.form = this.formBuilder.group({
      sizeBy: ['', Validators.required]
    });
  }
}

问题排查与解决方案

核心问题分析

  1. 输入属性未初始化:组件模板直接访问productItem.imageUrl,但测试中创建组件时未给@Input() productItem赋值,导致productItem为undefined,触发报错。
  2. TestBed配置错误:providers数组中的服务配置写法错误,多个服务不能放在同一个对象内,且CartService的配置关键字写错为uses,正确应为useValue。

具体修复步骤

  1. 初始化输入属性
    在测试的第二个beforeEach中,调用fixture.detectChanges()之前,给productItem赋值:

    beforeEach(() => {
      fixture = TestBed.createComponent(ProductItemComponent);
      component = fixture.componentInstance;
      // 给输入属性赋值
      component.productItem = FAKE_PRODUCTS[0];
      fixture.detectChanges();   
    });
    
  2. 修正TestBed服务配置
    将providers中的服务配置拆分为独立对象,修正关键字错误:

    providers:[
      { provide: WishlistItemService, useValue: wishlistitemServiceSpy },
      { provide: CartService, useValue: cartServiceSpy }
    ]
    
  3. 清理无效代码
    删除测试用例中无效的component.imageUrl代码,修正后的测试用例:

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

完成以上修改后,组件能正常获取productItem数据,模板渲染不会再触发undefined报错,单元测试可正常执行。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 01:12:32