1
votes

I am using following for my angular project Angular CLI: 10.2.3

Node: 12.22.1

I have following dependencies (for testing)

"devDependencies": {
    "@angular-devkit/build-angular": "^0.1002.0",
    "@angular/cli": "^10.2.3",
    "@angular/compiler-cli": "~10.0.2",
    "@ngneat/spectator": "^5.13.0",
    "@types/jest": "^26.0.23",
    "@types/node": "^12.11.1",
    "codelyzer": "^6.0.0-next.1",
    "jest": "^27.0.5",
    "jest-mock-extended": "^1.0.16",
    "jest-preset-angular": "^9.0.4",
    "ngx-deploy-npm": "^1.2.2",
    "protractor": "~7.0.0",
    "ts-node": "~8.3.0",
    "tslint": "~6.1.0",
    "typescript": "~3.9.5"
  }

I want to understand how to improve the coverage of my test so sonarqube report is showing better coverage.

I have one component as below. I have added "istanbul ignore next" comment for the constructor otherwise the coverage report shows that I am not covering those line of codes. I do not want to test private constructor, so using the above comment.

// Private class used by the component. do not want to test it.  
export class Summary {

  /* istanbul ignore next */
  constructor(
    public vDate: Date,
    public bookName: string,
    .
    .
  ) {
    }
}

export class SummaryComponent implements OnInit {

    ngOnInit(): void {
    .
    .
    }
    
    handleUserSelection(selection) {
        console.log("Handle User Selection");
        console.log("Selection: " + selection.selectedDateTo.year);
        console.log("Selection: " + selection.selectedDateFrom.month);
        if (selection.selectedDateTo instanceof Date && selection.selectedDateTo.getTime()) {
          this.currentDateTo = this.util.formatCalendarDateToDateStr(selection.selectedDateTo);     
        } else {
          this.currentDateTo = this.util.formatCalendarDateToDateStr(new Date(selection.selectedDateTo.year,selection.selectedDateTo.month - 1,selection.selectedDateTo.day));
        }
        if (selection.selectedDateFrom instanceof Date && selection.selectedDateFrom.getTime()) {
          this.currentDateFrom = this.util.formatCalendarDateToDateStr(selection.selectedDateFrom);
        } else {
          this.currentDateFrom = this.util.formatCalendarDateToDateStr(new Date(selection.selectedDateFrom.year,selection.selectedDateFrom.month - 1,selection.selectedDateFrom.day));
        }

        console.log(selection);
        .
        .
        
    }
}

Basically, I have a method in a component. My test file looks like this:

describe('SummaryComponent', () => {

  let spectator: Spectator<SummaryComponent>;
  const createComponent = createComponentFactory({
    component: SummaryComponent,
    declarations: [],
    imports: [HttpClientTestingModule,
              MatSnackBarModule,
              RouterTestingModule
             ],
    providers: [
      { provide: MatDialog, useClass: MatDialogMock },
    ],
    schemas: [NO_ERRORS_SCHEMA],
    mocks: [BasicAuthService,            
            SummaryDataService,            
           ],
    detectChanges: false
  });
  
  beforeEach(()=> {
    spectator= createComponent();    
  });
  
  it('should handle user selection', () => {    
    jest.spyOn(spectator.component, 'handleUserSelection').mockReturnValue(null);
    spectator.component.handleUserSelection(selection);
    expect(spectator.component.handleUserSelection).toHaveBeenCalled();
  });

});

In my coverage, it shows the method is not covered at all. enter image description here

How can I improve test, so that the method is covered?

UPDATE:

I tried to mock implement the function as below:

it('should handle user selection', () => {    
    jest.spyOn(spectator.component, 'handleUserSelection').mockImplementation(() => {
      
      let jSelection: any = {
        selectedDateFrom: {year: 2020, month: 5, day: 29},
        selectedDateTo: {year: 2020, month: 6, day: 30},        
      };

      console.log("Handle User Selection");
      console.log("Selection: " + jSelection.selectedDateTo.year);
      console.log("Selection: " + jSelection.selectedDateFrom.month);

      if (jSelection.selectedDateTo instanceof Date && jSelection.selectedDateTo.getTime()) {
        spectator.component.currentDateTo = spectator.component.util.formatCalendarDateToDateStr(jSelection.selectedDateTo);     
      } else {
        spectator.component.currentDateTo = spectator.component.util.formatCalendarDateToDateStr(new Date(jSelection.selectedDateTo.year,jSelection.selectedDateTo.month - 1,jSelection.selectedDateTo.day));
      }
      if (jSelection.selectedDateFrom instanceof Date && jSelection.selectedDateFrom.getTime()) {
        spectator.component.currentDateFrom = spectator.component.util.formatCalendarDateToDateStr(jSelection.selectedDateFrom);
      } else {
        spectator.component.currentDateFrom = spectator.component.util.formatCalendarDateToDateStr(new Date(jSelection.selectedDateFrom.year,jSelection.selectedDateFrom.month - 1,jSelection.selectedDateFrom.day));
      }

      console.log("Handle user selection Done!");

    });
    
    spectator.component.handleUserSelection(selection);
    expect(spectator.component.handleUserSelection).toHaveBeenCalled();
  });

Still the coverage shows the same i.e. the function is not covered at all?

what should I do to have the method covered?

1

1 Answers

0
votes

I meant this to be a comment, but sadly I don't have enough reputation.

Looking at your code snippet, I have a doubt that why you're mocking the exact method you are trying to test? should not we just mock the functions that your handleUserSelection needs to call. I believe that this way, your actual code of handleUserSelection never even gets executed during unit test, because it always returns the mocked implementation instead of actual function call.

My suggestion would be to try and call the handleUserSelection method without jest.spyOn and mock methods like spectator.component.util.formatCalendarDateToDateStr. After all, unit test should only focus on current block of code, and not the subsequent code which is getting called.