this.activatedRoute.queryParamMap.pipe 中的单元测试茉莉花错误

Unit test jasmine error in this.activatedRoute.queryParamMap.pipe

我有一个 Anuglar(版本 8)应用程序,它使用 ngrx/store 和 RxJs for angular 这个组件:

 export class CarComponent implements OnInit, OnDestroy {
      // subscriptions
      private unsubscribe$ = new Subject<void>();
    
  
      constructor(
        private activatedRoute: ActivatedRoute,
        private readonly store: Store<any>,
        private routingService: RoutingService,
        private readonly carService: CarService) {}
    
      ngOnInit(): void {
       
        this.activatedRoute.queryParamMap.pipe(
          takeUntil(this.unsubscribe$)).subscribe((paramMap: ParamMap) => {
          this.store
            .pipe(
              select(selectDog, { dog: paramMap.get('carCode')})
            )
            .subscribe((car: Car) => {
              this.setCar(Car);
            }).unsubscribe();
        });
      }}
}

我用 Karma 和 Jasmine 创建了一个单元测试:

import { ChangeDetectionStrategy } from '@angular/core';
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { ReactiveFormsModule } from '@angular/forms';
import { ActivatedRoute, Router } from '@angular/router';
import { RouterTestingModule } from '@angular/router/testing';
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
import { NgSelectModule } from '@ng-select/ng-select';
import { EffectsModule } from '@ngrx/effects';
import { Store, StoreModule } from '@ngrx/store';
import { Observable } from 'rxjs';

const scope = (): Scope => {
  return {
    carCode: '5288-5547-5247-4',
    brandCar: '480',
  };
};

describe('CarComponent ', () => {
  let activatedRoute: ActivatedRoute;
  let component: CarComponent;
  let fixture: ComponentFixture<CarComponent>;
  let store: Store<any>;
  let routingService: RoutingService;
  let router: Router;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      imports: [
        ReactiveFormsModule,
        RouterTestingModule,
        RouterTestingModule.withRoutes([]),
        SharedModule,
        StoreModule.forRoot({}),
        StoreModule.forFeature(stayRestrictionsFeatureKey, stayRestrictionReducer),
        StoreModule.forFeature(scopeFeatureKey, scopeReducer),
        StoreModule.forFeature(inventoryFeatureKey, inventoryReducer),
        StoreModule.forFeature(codeCarFeatureKey, codeCarReducer),
        StoreModule.forFeature('scope', scopeReducer),
        EffectsModule.forRoot([]),
        NgbModule,
        NgSelectModule,
        NgOptionHighlightModule
      ],
      providers: [{
        provide: ActivatedRoute,
        useValue: {
          snapshot: {
            queryParams: {
              carCode: 'ss'
            }
          }
        }
      },
        {
          provide: Router,
          useValue: {
            routerState: {
              snapshot : {
                url :  'month/day/123'
              }
            }
          }
        }]
    })
  }));

  beforeEach(() => {
    activatedRoute = TestBed.get(ActivatedRoute.prototype.queryParamMap.pipe());
    router = TestBed.get(Router);
    routingService = TestBed.get(RoutingService);
    store = TestBed.get(Store);
    spyOn(store, 'dispatch').and.callThrough();
    store.dispatch(new LoadScopeSuccess(scope()));

    fixture = TestBed.createComponent(CarComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('init', () => {
    spyOn(activatedRoute.queryParamMap, 'pipe').and.returnValue(new Observable());
    component.ngOnInit();
    const start = DateUtil.firstOfMonth();
    const end = endDate(start, 3);
    fixture.whenStable().then(_ => {
      expect(component.searchCriteriaForm.get('carCode').value).toEqual('5288-5547-5247-4');
      expect(component.searchCriteriaForm.get('brandCode').value).toEqual('480');
});
    });
});

但是当我启动测试时出现这个错误:

1) init
     CarComponent 
     TypeError: Cannot read property 'pipe' of undefined
    at Object.get queryParamMap [as queryParamMap] (http://localhost:9876/_karma_webpack_/C:/Root/root-projects/[...]/client/node_modules/@angular/router/fesm2015/router.js:2747:1)
    at UserContext.<anonymous> (http://localhost:9876/_karma_webpack_/src/app/monthly-view/monthly-view.component.spec.ts:90:59)
    at ZoneDelegate.invoke (http://localhost:9876/_karma_webpack_/C:/Root/root-projects/[...]/client/node_modules/zone.js/dist/zone-evergreen.js:359:1)
    at ProxyZoneSpec.push.../../node_modules/zone.js/dist/zone-testing.js.ProxyZoneSpec.onInvoke (http://localhost:9876/_karma_webpack_/C:/Root/root-projects/[...]/client/node_modules/zone.js/dist/zone-testing.js:308:
1)
    at ZoneDelegate.invoke (http://localhost:9876/_karma_webpack_/C:/Root/root-projects/[...]/client/node_modules/zone.js/dist/zone-evergreen.js:358:1)
    at Zone.run (http://localhost:9876/_karma_webpack_/C:/Root/root-projects/[...]/client/node_modules/zone.js/dist/zone-evergreen.js:124:1)
[...]

我试过模拟激活路由,但我还没有找到解决方案。您有解决方案来绕过这个问题吗?非常感谢。

您必须模拟 activatedRoute.queryParamMap,不幸的是,由于它不是一种方法,因此您不能使用 pipe。我会这样做:

import { ChangeDetectionStrategy } from '@angular/core';
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { ReactiveFormsModule } from '@angular/forms';
import { ActivatedRoute, Router } from '@angular/router';
import { RouterTestingModule } from '@angular/router/testing';
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
import { NgSelectModule } from '@ng-select/ng-select';
import { EffectsModule } from '@ngrx/effects';
import { Store, StoreModule } from '@ngrx/store';
import { Observable } from 'rxjs';

const scope = (): Scope => {
  return {
    carCode: '5288-5547-5247-4',
    brandCar: '480',
  };
};

describe('CarComponent ', () => {
  let activatedRoute: ActivatedRoute;
  let component: CarComponent;
  let fixture: ComponentFixture<CarComponent>;
  let store: Store<any>;
  let routingService: RoutingService;
  let router: Router;
  // !! add this line
  const mockQueryParamMap = new BehaviorSubject<any>({
    get(value: string) => {
       if (value === 'carCode') {
         return '123'; // your mock for `carCode`
       } else {
         return '456';
       }
    }
  });

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      imports: [
        ReactiveFormsModule,
        RouterTestingModule,
        RouterTestingModule.withRoutes([]),
        SharedModule,
        StoreModule.forRoot({}),
        StoreModule.forFeature(stayRestrictionsFeatureKey, stayRestrictionReducer),
        StoreModule.forFeature(scopeFeatureKey, scopeReducer),
        StoreModule.forFeature(inventoryFeatureKey, inventoryReducer),
        StoreModule.forFeature(codeCarFeatureKey, codeCarReducer),
        StoreModule.forFeature('scope', scopeReducer),
        EffectsModule.forRoot([]),
        NgbModule,
        NgSelectModule,
        NgOptionHighlightModule
      ],
      providers: [{
        provide: ActivatedRoute,
        useValue: {
          snapshot: {
            queryParams: {
              carCode: 'ss'
            }
          }
        }
      },
        {
          provide: Router,
          useValue: {
            routerState: {
              snapshot : {
                url :  'month/day/123'
              }
            },
            // !! add this line
            queryParamMap: mockQueryParamMap,
          },
        }]
    })
  }));

  it('init', () => {
    // this is why we use BehaviorSubject so we have full control
    // on what to push into the subscription
    mockQueryParamMap.next({
    get(value: string) => {
       if (value === 'carCode') {
         return '5288-5547-5247-4'; // your mock for `carCode`
       } else {
         return '456';
       }
    }
   });
    component.ngOnInit();
    const start = DateUtil.firstOfMonth();
    const end = endDate(start, 3);
    fixture.whenStable().then(_ => {
      expect(component.searchCriteriaForm.get('carCode').value).toEqual('5288-5547-5247-4');
      expect(component.searchCriteriaForm.get('brandCode').value).toEqual('480');
});
    });