在 sonarqube 中使用 jest 和旁观者覆盖组件方法的 Angular 打字稿测试

Posted

技术标签:

【中文标题】在 sonarqube 中使用 jest 和旁观者覆盖组件方法的 Angular 打字稿测试【英文标题】:Angular typescript test with jest and spectator cover component methods in sonarqube 【发布时间】:2021-09-16 07:20:06 【问题描述】:

我正在为我的 Angular 项目使用以下内容 角 CLI:10.2.3

节点:12.22.1

我有以下依赖项(用于测试)

"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"
  

我想了解如何提高我的测试覆盖率,以便 sonarqube 报告显示更好的覆盖率。

我有一个组件如下。我为构造函数添加了“istanbul ignore next”注释,否则覆盖率报告显示我没有覆盖那些代码行。我不想测试私有构造函数,所以使用上面的注释。

// 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);
        .
        .
        
    

基本上,我在组件中有一个方法。我的测试文件如下所示:

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

);

在我的报道中,它表明该方法根本没有被报道。

我怎样才能改进测试,以便覆盖该方法?

更新:

我尝试模拟实现如下功能:

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

覆盖率仍然显示相同,即根本没有覆盖该功能?

我应该怎么做才能覆盖该方法?

【问题讨论】:

【参考方案1】:

我的意思是评论,但遗憾的是我没有足够的声誉。

查看您的代码 sn-p,我怀疑您为什么要模拟您要测试的确切方法?我们不应该只是模拟您的handleUserSelection 需要调用的函数。我相信这样一来,handleUserSelection 的实际代码甚至不会在单元测试期间被执行,因为它总是返回模拟的实现而不是实际的函数调用。

我的建议是尝试在没有jest.spyOn 的情况下调用handleUserSelection 方法,并模拟spectator.component.util.formatCalendarDateToDateStr 之类的方法。毕竟,单元测试应该只关注当前的代码块,而不是被调用的后续代码。

【讨论】:

以上是关于在 sonarqube 中使用 jest 和旁观者覆盖组件方法的 Angular 打字稿测试的主要内容,如果未能解决你的问题,请参考以下文章

将 JUnit 报告推送到 Sonarqube

SonarQube 单元测试报告不显示 Javascript 文件

为啥基于 jest 的单元测试现在会导致 0% 的测试覆盖率?

使用 Powermock 在 Sonarqube 中配置 jacoco 以进行集成和单元测试报告

如何在 React 和 Webpack 中使用 Jest

谈团队建设之一: 旁观者效应