2

I`m trying to write unit tests to cover every line of my code. I have two lines of my code that are not covered.

I can`t understand where I make a mistake and how I can cover these lines of code?

Here is an image of not covered lines of code:

code coverage screenshot

Product-item.spect.ts

import { HttpClientTestingModule } from '@angular/common/http/testing';
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { async, ComponentFixture,TestBed } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { ProductService } from '../../services/product.service';
import { ProdItemComponent } from './product-item.component';

describe('ProdItemComponent', () => {
  let component: ProdItemComponent;
  let fixture: ComponentFixture<ProdItemComponent>;
  let productService: ProductService;
  let mockProductItem: any;

  beforeEach(async(() => {

    TestBed.configureTestingModule({
      declarations: [ProductItemComponent],
      imports: [HttpClientTestingModule, RouterTestingModule],
      providers: [ProductService],
      schemas: [CUSTOM_ELEMENTS_SCHEMA]
    })
      .compileComponents();
  }));

  beforeEach(() => {

    mockProductItem= {
      id: "c7336f01-5219-4631-a865-af1fa9837766",
      title:"Carpet",
      description:"A soft Carpet"
    }

    fixture = TestBed.createComponent(ProductItemComponent);
    component = fixture.componentInstance;
    productService = TestBed.inject(ProductService);
    fixture.detectChanges();
    component.productItem = mockProductItem;
    component.selectedItemId = component.mockProductItem.id;

  });

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

  it('should call delete method', () => {
    component.onDelete();
    fixture.detectChanges();
    productService.deleteProduct(component.selectedItemId).subscribe(selectedItemId => {
      expect(selectedItemId).toBeTruthy();
      const spy = spyOn(component.fetchDataEventEmitter, 'emit');
      expect(spy).toHaveBeenCalled();
    })
  });
  expect(component.displayConfirmDialog).toBe(false);
});

Product-service.ts

 deleteProduct(productId: string) {
    return this.httpClient.delete(
      this.BASE_URL +
      this.DELETE_ITEM_URL(
        productId
      )
    )
  }

Product-component.ts

onDelete(): void {
    if (this.selectedItemId) {
      this.productService.deleteProduct(this.selectedItemId).subscribe(res => {
        this.fetchDataEventEmitter.emit();
      });
    }
    this.displayConfirmDialog = false;
  }

Gabriel Sereno
  • 845
  • 1
  • 5
  • 6
Nikolay
  • 59
  • 1
  • 8

3 Answers3

3

The last expect here is out of place, it should be moved one line up.

  it('should call delete method', () => {
    component.onDelete();
    fixture.detectChanges();
    productService.deleteProduct(component.selectedItemId).subscribe(selectedItemId => {
      expect(selectedItemId).toBeTruthy();
      const spy = spyOn(component.fetchDataEventEmitter, 'emit');
      expect(spy).toHaveBeenCalled();
    });
    // should go here
    expect(component.displayConfirmDialog).toBe(false);
  });
  // expect(component.displayConfirmDialog).toBe(false);

You need to mock ProductService using a spyObject because you don't want to be doing actual http calls in your unit tests.

  let component: ProdItemComponent;
  let fixture: ComponentFixture<ProdItemComponent>;
  let productService: ProductService;
  let mockProductItem: any;
  // add this line
  let mockProductService: jasmine.SpyObj<ProductService>;

  beforeEach(async(() => {
    // create a spy object
    // the first string is an identifier and is optional. The array of strings
    // are the public methods that you would like to mock.
    mockProductService = jasmine.createSpyObj<ProductService>('ProductService', ['deleteProduct']);
    TestBed.configureTestingModule({
      declarations: [ProductItemComponent],
      // get rid of HttpClientTestingModule since we are mocking
      // ProductService now
      imports: [/*HttpClientTestingModule*/, RouterTestingModule],
      // when the component asks for ProductService, give the mocked one
      providers: [{ provide: ProductService, useValue: mockProductService }],
      schemas: [CUSTOM_ELEMENTS_SCHEMA]
    })
      .compileComponents();
  }));

...
it('should call delete method', () => {
    // make the deleteProduct method return an observable of empty string
    mockProductService.deleteProduct.and.returnValue(of(''));
    // spy on the emission
    const emitSpy = spyOn(component.fetchDataEventEmitter, 'emit');

    component.onDelete();
    fixture.detectChanges();

    expect(emitSpy).toHaveBeenCalled();
    expect(component.displayConfirmDialog).toBeFalse();
  });

AliF50
  • 16,947
  • 1
  • 21
  • 37
0

It looks like you are creating the spy object too late in the execution. If you create the spy object before calling productService.delete but leave the expect assertion where it is, that should solve the issue.

byte-this
  • 204
  • 1
  • 2
  • I moved to the top this "const spy = spyOn(component.fetchDataEventEmitter, 'emit') " but is the same these lines of code it`s still not covered. SPEC HAS NO EXPECTATIONS should call delete method – Nikolay May 31 '21 at 19:16
0

Try to assign a simple observable to your fetchDataEventEmitter property and move it to the top of the case:

 it('should call delete method', () => {
    component.fetchDataEventEmitter = of({})
    component.onDelete();
    fixture.detectChanges();
    productService.deleteProduct(component.selectedItemId).subscribe(selectedItemId => {
      expect(selectedItemId).toBeTruthy();
    })
  });
Roman A.
  • 686
  • 5
  • 25
  • I got this Error when I try to assign a simple observable to fetchDataEventEmitter . - Type 'Observable<{}>' is missing the following properties from type 'EventEmitter': emit, observers, closed, isStopped, and 7 more. – Nikolay May 31 '21 at 19:36
  • Doest `spyOn(component. fetchDataEventEmitter, 'emit');` not work? – Roman A. May 31 '21 at 19:39