【问题标题】:How to do unit testing for getCurrentNavigation().extras.state in angular 7如何在角度 7 中对 getCurrentNavigation().extras.state 进行单元测试
【发布时间】:2019-12-28 10:02:06
【问题描述】:

我正在尝试使用 jasmine 为 getCurrentNavigation().extras.state 编写单元测试。

为了解决这个问题,我尝试了窥探这个路由器的方法。

我的组件文件,

@Component({
  selector: 'app-location-list',
  templateUrl: './location-list.component.html',
  styleUrls: ['./location-list.component.scss']
})
export class LocationListComponent implements OnInit{

  locationId;
  locationName;
  constructor(private activatedRoute: ActivatedRoute, private router: Router) {
    if (this.router.getCurrentNavigation().extras.state) {
      this.locationId = this.router.getCurrentNavigation().extras.state.locationId;
    }
    if (this.router.getCurrentNavigation().extras.state) {
      this.locationName = this.router.getCurrentNavigation().extras.state.locationName;
    }
  }
  ngOnInit() { }
}

我的规格文件,

describe('LocationListComponent ', () => {
  let component: LocationListComponent ;
  let fixture: ComponentFixture<LocationListComponent >;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      imports: [
        RouterTestingModule
      ],
      declarations: [
        LocationListComponent 
      ],
      providers: []
    })
      .compileComponents();
  }));

  beforeEach(() => {
    fixture = TestBed.createComponent(LocationListComponent );
    component = fixture.componentInstance;
    spyOn(Router.prototype, 'getCurrentNavigation').and.returnValues({ 'extras': { 'state': { 'locationId': 100, 'locationName': "UK" } } });
    fixture.detectChanges();
  });

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

但我收到以下错误,

TypeError: Cannot read property 'extras' of null

谁能帮我解决这个问题。我正在使用Angular 7.2

【问题讨论】:

    标签: angular typescript jasmine karma-jasmine angular7-router


    【解决方案1】:

    您可以使用 stubuseClass 轻松做到这一点,如果您可以在单独的文件和 export class RouterStub 中创建它,也可以在其他 spec 文件中重复使用,尝试:

    spec 文件中创建一个stub,其方法与Router 相同:

    class RouterStub{
     getCurrentNavigation(){
       return {
          extras: {
             state:{
               locationId: 'someId',
               locationName: 'someName'
             }
           }
         }
       }
    }
    

    beforeEach() 块中:

    describe('LocationListComponent ', () => {
      let component: LocationListComponent ;
      let fixture: ComponentFixture<LocationListComponent >;
    
      beforeEach(async(() => {
        TestBed.configureTestingModule({
          imports: [
            RouterTestingModule
          ],
          declarations: [
            LocationListComponent 
          ],
          providers: [ {provide: Router, useClass: RouterStub}]
        })
          .compileComponents();
      }));
    
      beforeEach(() => {
        fixture = TestBed.createComponent(LocationListComponent );
        component = fixture.componentInstance;
        fixture.detectChanges();
      });
    
      it('should create', () => {
        expect(component).toBeTruthy();
      });
    });
    

    【讨论】:

    • 用你的方法将是 undefined getCurrentNavigation()
    • 你如何用不同的属性测试它?
    • 您需要在您的beforeEach 中监视路由器的getCurrentNavigation 方法 - 否则,您将收到cannot read property 'root' of undefined 的错误。见this post
    • 现在假设这个页面有this.router.navigate(['page-name'], navigationExtras),那么这将如何工作,因为这需要类似stackoverflow.com/a/40301110 这样的东西,我们不能同时编写两个相同的提供程序。
    【解决方案2】:

    如果我们尝试同时使用RouterTestingModule{provide: Router, useClass: RouterStub} 会抛出cannot read property 'root' of undefined 的错误

    所以我们可以直接为 Route 创建 spy 并返回它的值

    describe('LocationListComponent', () => {
      let component: LocationListComponent ;
      let fixture: ComponentFixture<LocationListComponent>;
      let router: jasmine.SpyObj<Router>;
    
      beforeEach(async(() => {
        TestBed.configureTestingModule({
          imports: [
            RouterTestingModule
          ],
          declarations: [
            LocationListComponent 
          ],
        })
          .compileComponents();
      }));
    
      beforeEach(() => {
        router = TestBed.get(Router);
        spyOn(router, 'getCurrentNavigation').and.returnValue({ extras: { state: { message: 'msg'} } } as any);
        fixture = TestBed.createComponent(LocationListComponent);
        component = fixture.componentInstance;
        fixture.detectChanges();
      });
    
      it('should create', () => {
        expect(component).toBeTruthy();
      });
    });
    

    【讨论】:

    • 我喜欢这种方法,但在 spyOn 行上它说参数 is not assignable to parameter of type 'Navigation': is missing the following properties from type 'Navigation': id, initialUrl, extractedUrl, trigger, previousNavigation
    • @A.Wan 你可以在对象后面加上“as any”,这样它就会被接受。我认为这是在测试中模拟部分对象的常用方法。
    • @A.Wan 感谢这个问题,我已经更新了答案
    • 就我而言,当我解决所有问题时,它不会抛出此类错误。
    • 谢谢!你可以(也许应该)使用as SpyObj&lt;Router&gt; vs as any
    【解决方案3】:

    router.getCurrentNavigation() 的单元测试可以通过 3 个简单的步骤完成:

    1. 创建模拟路由器对象:

      const mockRouter = {
        getCurrentNavigation: jasmine.createSpy('getCurrentNavigation')
      };
      
    2. providers数组中注入这个:

      providers: [{ provide: Router, useValue: mockRouter }]
      
    3. beforeEach() 函数中返回所需的模拟值

      mockRouter.getCurrentNavigation.and.returnValue({
        extras: {
          state: {
            test: ''
          }
        }
      });
      

    【讨论】:

      【解决方案4】:

      你需要从fixture获取路由实例

      router = fixture.debugElement.injector.get(Router);
      

      【讨论】:

        猜你喜欢
        • 2020-07-10
        • 1970-01-01
        • 2019-05-15
        • 2021-03-11
        • 1970-01-01
        • 1970-01-01
        • 2015-10-05
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多