angularunit-testingkarma-runner

Angular Karma/Jasmine Unit Test Cannot Read Properties Of Undefined Readin imageUrl


I have been tackling an error for a couple of days in my unit testing. Everything I have tried thus far from my research does not seem to resolve the issue. Three of my components are failing unit test with same error cannot read properties of undefined reading imageUrl which is a property of my models. The error is coming from my template. Some of the research I have done states that this property must be mentioned in the test bed, based on what I read this is not working for me. My components are not being created because of this error. I commented out some test until I get this error resolved. Can someone tell me what I am dong wrong or point me in the right direction. It would be greatly appreciated. Code snippet included.

Model 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']);
  //const cartServiceSpy= jasmine.createSpy('fetch').and.returnValue(okResponse);
  //cartServiceSpy.addProductToCart.and.returnValue([FAKE_PRODUCTS]) 


  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();
 });


});

here is a snippet of 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]
});
   

}

Solution

  • The problem is that in your component you are expecting the input productItem to be defined, as indicated by usage of the definite assignment assertion !. However in your unit tests, you don't define the input. To fix it, set the input in your test:

    beforeEach(() => {
      fixture = TestBed.createComponent(ProductItemComponent);
      component = fixture.componentInstance;
      fixture.componentRef.setInput('productItem', FAKE_PRODUCTS[0]);
      fixture.detectChanges();   
    });
    

    You can additionally mark the input as required which will throw a descriptive error if the input is not provided.

    Alternatively you can remove the ! operator and fix the resulting type errors, so the component can handle the productItem being undefined.