这个角度4单元测试是否符合当前惯例?

时间:2017-10-25 21:02:24

标签: angular unit-testing typescript

我在ng 4中对基本@Injectable服务进行单元测试。教程使用不同的约定(例如使用TestBed vs debugElement.injector)。当这些测试通过时,我的设置感觉拼凑在一起,可能会使用不良的约定。

以下是我的担忧:

  1. providers的声明似乎很详细,但providers: [MockProgressServer]导致了业力错误。
  2. 测试组件时是否应始终包含第一个测试expect(component).ToBeTruthy?这似乎是由其他模块中的ng cli自动生成的。
  3. fixture.debugElement.injector.get(ProgressService)似乎与TestBed.get(ProgressService)一致。哪个更适合?
  4. 根据经验,这样的测试是否总是被声明为async()
  5. 我们正在测试的课程ProgressService progress.service.ts。它很简单,包含一个currentState字段getterssetters

    import { Injectable } from '@angular/core';
    
    @Injectable()
    export class ProgressService {
        private currentState: string = '1';
    
        constructor() {
        }
    
        setCurrentState(state: string) {
            this.currentState = state;
        }
    
        getCurrentState(){
            return this.currentState
        }    
    }
    

    因为这个类很简单,所以相应的Mock不会覆盖任何内容。

    import {ProgressService} from "../../../progress.service";
    
    export class MockProgressService extends ProgressService {
    }
    

    我拼凑了以下progress.spec.ts课程来对吸气剂和制定者进行单元测试。

    import { async, ComponentFixture, TestBed } from '@angular/core/testing';
    
    import { ProgressComponent } from './progress.component';
    import {MockProgressService} from "../shared/services/progress/progress.mock1";
    import {ProgressService} from "../progress.service";
    
    describe('ProgressComponent', () => {
      let component: ProgressComponent;
      let fixture: ComponentFixture<ProgressComponent>;
      let mockService: ProgressService;
    
      beforeEach(async() => {
        TestBed.configureTestingModule({
          declarations: [ ProgressComponent ],
          providers: [{provide: ProgressService, useValue: new MockProgressService()}]
        }).compileComponents()
    
        fixture = TestBed.createComponent(ProgressComponent);
        component = fixture.componentInstance;
        mockService = fixture.debugElement.injector.get(ProgressService)
    
        fixture.detectChanges();
      });
    
      fit('The component should be created', () => {
        expect(component).toBeTruthy();
      });
    
      fit('ProgressService default currentState is "1"', () =>{
        expect(mockService.getCurrentState()).toEqual('1')
      })
    
      fit('ProgressService new currentState is "5"', () =>{
        mockService.setCurrentState('5')
        expect(mockService.getCurrentState()).toEqual('5')
      })
    });
    

    npm test表明这些传递

    enter image description here

    感谢。我希望在继续讨论非常重要的案件之前将其锁定。

    PS,有没有办法改变Karma浏览器报告的样式? (EG使字体更大?)

    基于estus答案更新的解决方案

    拆分测试模块。 首先,测试服务:

    import {async, ComponentFixture, getTestBed, TestBed} from '@angular/core/testing';
    
    import { ProgressComponent } from './progress.component';
    import {ProgressService} from "../progress.service";
    
    describe('ProgressComponent', () => {
      let injector: TestBed;
      let service: ProgressService;
    
      beforeEach(async() => {
        TestBed.configureTestingModule({
         providers: [ProgressService]
        }).compileComponents()
    
        injector = getTestBed()
        service= injector.get(ProgressService)    
    
      });    
      fit('ProgressService default currentState is "1"', () =>{
        expect(service.getCurrentState()).toEqual('1')
      })
    
      fit('ProgressService new currentState is "5"', () =>{
        service.setCurrentState('5')
        expect(service.getCurrentState()).toEqual('5')
      })
    });
    

    两件事:

    1. 我对provider / TestBed.get()的使用是否正确?为什么我不能自己实例化ProgressService实例? TestBed是否确保创建单例?

    2. 不确定fakeAsync的建议适合...

    3. 接下来,测试组件

      import {async, ComponentFixture, getTestBed, TestBed} from '@angular/core/testing'
      
      import { ProgressComponent } from './progress.component'
      import {MockProgressService} from "../shared/services/progress/progress.mock1"
      import {ProgressService} from "../progress.service"
      
      describe('ProgressComponent', () => {
        let injector: TestBed
        let mockService: ProgressService
        let fixture: ComponentFixture<ProgressComponent>
        let component: ProgressComponent
      
        beforeEach(async() => {
          TestBed.configureTestingModule({
            declarations: [ ProgressComponent ],
            providers: [{provide: ProgressService, useClass: MockProgressService}]
          }).compileComponents()
      
          injector = getTestBed()
          fixture = TestBed.createComponent(ProgressComponent)
          component = fixture.componentInstance
          mockService = injector.get(ProgressService)
      
          fixture.detectChanges();
        });
      
         fit('The component should be created', () => {
           expect(component).toBeTruthy();
         });
      
         fit('The service should be an instance of MockService', () => {
           expect(mockService instanceof MockProgressService).toBeTruthy()
         })
      });
      

1 个答案:

答案 0 :(得分:2)

通常,服务测试不应包含declarationsTestBed.createComponent。组件会产生额外的移动部件,并可能影响测试结果。 ProgressService当然不需要在自己的测试中嘲笑。实际上,ProgressService default currentState等和The component should be created属于两种不同的试验台设置。一个有providers: [ProgressService],另一个有declarations: [ ProgressComponent ]MockProgressService

此处使用providers: [MockProgressService]不正确,因为正在使用的DI令牌为ProgressService,并且必须嘲笑此提供程序。 providers应该是:

providers: [{provide: ProgressService, useClass: MockProgressService}]

expect(component).ToBeTruthy断言不是必需的,但可能有用,因为测试环境中有一些条件会导致beforeEach阻止静默失败,而The component should be created测试是可以确定这个的问题毫不含糊。

fixture.debugElement.injector.get(ProgressService)可以安全地替换为TestBed.get(ProgressService) 此处,这就是TestBed.getinject助手的用途。但是这两个不可互换因为TestBed.get从根注入器检索服务实例,而fixture.debugElement.injector是组件注入器并且可以访问特定于组件的实例。此外,如果这是没有组件的试验台,则不会有fixture

根据经验,测试应该使用fakeAsync助手 - 只是因为它会导致零延迟并完成工作。它应该开箱即用。如果存在真正异步的测试(例如加载的模板),则fakeAsync会导致有意义的错误,建议将其更改为async