I made some updates to an Angular component and it broke some unit tests. All test specs are broken so to me it seems like something to do with initialization in the beforeEach
calls. I have tried to research the problem extensively but haven't had any success.
The error I get back from running my unit tests is:
TypeError: Cannot read property 'subscribe' of undefined
The only calls made to subscribe
are:
this.ruleDataFieldOption$.subscribe
...
this.submitted$.subscribe
...
this.dataFieldControl.valueChanges.subscribe
I tried to explicitly initialize ruleDataFieldOption$
by setting it to of(mockRuleDataFields)
but that did not work. I also tried moving the block of code from the second beforeEach
call into the promise of the async beforeEach
call (i.e. .compileComponents().then( () => {...} )
and that didn't work either.
client-rules-condition.component.spec.ts
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.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;
}
}
}
});
.
.
.
I am confident that it is something minor and I just need my unit tests to pass.
spyOn(mockStore, 'dispatch')
should probably return anObservable
. – The Head Rush