角度单元测试错误-无法读取未定义的属性“订阅”

时间:2019-04-01 16:53:08

标签: angular typescript unit-testing karma-jasmine

我对Angular组件进行了一些更新,但它破坏了一些单元测试。所有测试规范均已损坏,因此对我来说似乎与beforeEach调用中的初始化有关。我已经尝试过广泛研究该问题,但是没有取得任何成功。

我从运行单元测试中得到的错误是: TypeError: Cannot read property 'subscribe' of undefined

subscribe的唯一呼叫是:

this.ruleDataFieldOption$.subscribe
...
this.submitted$.subscribe
...
this.dataFieldControl.valueChanges.subscribe

我试图通过将ruleDataFieldOption$设置为of(mockRuleDataFields)来显式地初始化beforeEach,但这没用。我还尝试将代码块从第二个beforeEach调用移到异步.compileComponents().then( () => {...} )调用的承诺中(即const mockDataFieldService = { getRuleDataFields: () => {} } as RuleDataFieldService; const mockStore = { select: td.function('.select'), dispatch: td.function('.dispatch'), pipe: td.function('.pipe'), } as Store<any>; let dispatch; let pipe; const mockConfigService = { getConfiguration: td.func(() => { return mockNotificationConfig }) } as ConfigService; const mockNotificationConfig = { Env: null, apiBaseUrl: 'blah' } as MclNotificationConfig; const mockRuleDataFields: RuleDataFieldDefinition[] = [ {name: 'data field 1', dataFieldId: 'mock data field guid', category: 'mock category'} as RuleDataFieldDefinition ]; fdescribe('ClientRulesConditionComponent', () => { let component: ClientRulesConditionComponent; let fixture: ComponentFixture<ClientRulesConditionComponent>; beforeEach(async(() => { TestBed.configureTestingModule({ declarations: [ ClientRulesConditionComponent ], imports: [ReactiveFormsModule], schemas: [CUSTOM_ELEMENTS_SCHEMA], providers: [ {provide: Store, useFactory: () => mockStore}, {provide: RuleDataFieldService, useValue: mockRuleDataFields}, HttpClientTestingModule, // {provide: ConfigService, useFactory: () => mockConfigService}, // {provide: MclNotificationConfig, useFactory: () => mockNotificationConfig}, // HttpHandler, HttpClient ] }) .compileComponents(); })); beforeEach(() => { dispatch = spyOn(mockStore, 'dispatch'); pipe = spyOn(mockStore, 'pipe').and.callThrough(); fixture = TestBed.createComponent(ClientRulesConditionComponent); component = fixture.componentInstance; const fb: FormBuilder = new FormBuilder(); component.conditionForm = fb.group({ name: fb.control('', [Validators.required]), triggerWhen: fb.control('', [Validators.required]), conditions: fb.array([ fb.group({ dataField: fb.control('df1'), conditionToMatch: fb.control('ctm1'), value: fb.control('v1') }), fb.group({ dataField: fb.control('df2'), conditionToMatch: fb.control('ctm2'), value: fb.control('v2') }) ]) }); component.indexInConditionArray = 1; component.submitted$ = of(false); component.ruleDataFieldOption$ = of(mockRuleDataFields); fixture.detectChanges(); }); it('should create', () => { expect(component).toBeTruthy(); // Confirm conditionToMatch and dataField get actions have been dispatched expect(dispatch).toHaveBeenCalledTimes(2); // Confirm that we load the correct set of form controls and they have the correct value associated with them const conditionsArray = component.conditionForm.controls['conditions'] as FormArray; const conditionGroup = conditionsArray.at(component.indexInConditionArray) as FormGroup; expect(component.dataFieldControl).toEqual(conditionGroup.controls['dataField'] as FormControl); expect(component.dataFieldControl.value).toEqual('df2'); expect(component.matchingConditionControl).toEqual(conditionGroup.controls['conditionToMatch'] as FormControl); expect(component.matchingConditionControl.value).toEqual('ctm2'); expect(component.valueControl).toEqual(conditionGroup.controls['value'] as FormControl); expect(component.valueControl.value).toEqual('v2'); // Confirm the conditionToMatch and dataField selectors were wired up expect(pipe).toHaveBeenCalledTimes(2); // Confirm the expected value for the 'submitted' flag got emitted expect(component.submitted).toBeFalsy(); }); }); . . . ,这也不起作用。

client-rules-condition.component.spec.ts

@Component({
  selector: 'nd-client-rules-condition',
  templateUrl: './client-rules-condition.component.html',
  styleUrls: ['./client-rules-condition.component.scss']
})
export class ClientRulesConditionComponent implements OnInit {

  @Input() conditionForm: FormGroup;
  @Input() indexInConditionArray: number;
  @Input() submitted$: Observable<boolean>;
  @Output() removeCondition: EventEmitter<number> = new EventEmitter();

  conditionToMatchOption$: Observable<ConditionToMatch[]>;

  valueControl: FormControl;
  matchingConditionControl: FormControl;
  dataFieldControl: FormControl;

  private static NPI_DATA_FIELD_GUID: string;

  validValuePattern: string;
  invalidValueError: string;

  // Emits the candidate rule fields that eventually come back from the database.
  //    The 'data fields' combo box in the markup is wired up to this.
  ruleDataFieldOption$: Observable<RuleDataFieldDefinition[]>;

  // Tracks whether the parent form has been submitted.
  submitted: boolean;

  constructor(private _store: Store<state.AppState>) { }

  ngOnInit() {
    this._store.dispatch(new GetConditionToMatchOptions(''));

    // Dispatch Action that causes the rule-data-field options to get retrieved from the server and slammed into the global state.
    this._store.dispatch(new GetRuleDataFieldsOptions(''));

    this.conditionToMatchOption$ = this._store.pipe(select(getConditionToMatchOption));

    // Wire up the selector that cause the rule-data-field options to get pulled from the global state when they are set/updated
    this.ruleDataFieldOption$ = this._store.pipe(select(getRuleDataFieldsSelector));

    // Load GUID for NPI field to use to distinguish between different Data Field dropdown items to apply different validation in Value To Match
    this.ruleDataFieldOption$.subscribe(dataFields => {
      if (ClientRulesConditionComponent.NPI_DATA_FIELD_GUID == null) {
        for (let df of dataFields) {
          if (df.name.toLowerCase().includes('npi') || df.name.toLowerCase().includes('national provider identifier')) {
            ClientRulesConditionComponent.NPI_DATA_FIELD_GUID = df.dataFieldId.toUpperCase();
            break;
          }
        }
      }
    });
    .
    .
    .

client-rules-condition.component.ts

[add_to_cart id='123']

我相信这是次要的事情,我只需要通过单元测试即可。

1 个答案:

答案 0 :(得分:0)

尝试一下,在每个declare的新observable之一中。

component.ruleDataFieldOption$ = new Observable<ConditionToMatch[]>;

我不知道是否有相同的规则适用于作为对象的可观察对象本身,但是您可以尝试一下。

相关问题