Angular限制小数位数指令测试:排查测试用例缺失环节
问题:Angular小数限制指令测试用例未按预期工作
我正在测试一个用于控制小数分隔符及允许小数位数的Angular指令InstrumentDecimalsDirective,该指令监听ngControl的valueChanges,当输入小数位数超出限制时将值回退为旧值。我已创建测试组件InstrumentDecimalTestComponent及对应测试用例,但测试未按预期工作,请指出测试用例中的缺失部分。
指令代码
@Directive({ selector: '[restrictedDecimals]' }) export class InstrumentDecimalsDirective implements OnInit { @Input() decimals = null; @Input() separator = ','; valueSubscription: Subscription; constructor(@Self() @Optional() public ngControl: NgControl) {} ngOnInit(): void { this.ngControl.valueChanges.pipe(distinctUntilChanged(), pairwise()).subscribe(([oldValue, newValue]) => { this.runCheck(oldValue, newValue); }); } ... }
测试组件模板
<input type="text" restrictedDecimals decimals="2" />
测试用例代码
describe('InstrumentDecimalsDirective', () => { let fixture: ComponentFixture<any>; beforeEach(async () => { const NG_CONTROL_PROVIDER = { provide: NgControl, useClass: class extends NgControl { control = new FormControl(); viewToModelUpdate() {} } }; const testModuleMetadata: TestModuleMetadata = { imports: [ReactiveFormsModule, FormsModule], declarations: [InstrumentDecimalTestComponent, InstrumentDecimalsDirective] }; fixture = TestBed.configureTestingModule(testModuleMetadata).overrideDirective(InstrumentDecimalsDirective, { add: { providers: [NG_CONTROL_PROVIDER] }, }).createComponent(InstrumentDecimalTestComponent); fixture.detectChanges(); }); it('should reject input with more decimals than allowed', fakeAsync(() => { const input = fixture.debugElement.queryAll(By.directive(InstrumentDecimalsDirective))[0].nativeElement; const inputEvent = new Event('input'); input.value = '1,12'; input.dispatchEvent(inputEvent); input.value = '1,123'; input.dispatchEvent(inputEvent); tick(500); fixture.detectChanges(); expect(input.value).toBe('1,12'); })); });
测试用例的缺失点及修复方案
未同步表单控件值与输入框视图值
直接修改input.value不会同步到ngControl的FormControl中,而指令监听的是ngControl.valueChanges,因此只有控件值更新时才会触发校验逻辑。需要通过指令实例直接操作FormControl来设置值:it('should reject input with more decimals than allowed', () => { const inputEl = fixture.debugElement.query(By.directive(InstrumentDecimalsDirective)); const directive = inputEl.injector.get(InstrumentDecimalsDirective); const input = inputEl.nativeElement; // 先设置合法值到控件 directive.ngControl.control.setValue('1,12'); fixture.detectChanges(); // 再设置非法值 directive.ngControl.control.setValue('1,123'); fixture.detectChanges(); expect(directive.ngControl.control.value).toBe('1,12'); expect(input.value).toBe('1,12'); });忽略了
pairwise操作符的触发条件pairwise需要至少两个值才会发射数据,若控件初始值为null/undefined,第一次设置'1,12'不会触发订阅回调,第二次设置'1,123'才会触发。建议在测试前给控件设置初始空值:beforeEach(() => { // ... 现有初始化代码 const directive = fixture.debugElement.query(By.directive(InstrumentDecimalsDirective)).injector.get(InstrumentDecimalsDirective); directive.ngControl.control.setValue(''); fixture.detectChanges(); });不必要的
fakeAsync与tick
指令的valueChanges是同步流(无异步操作符),tick(500)属于冗余操作,直接移除即可,同步执行校验逻辑。未验证视图与模型的双向同步
指令的runCheck若仅回退了控件值,可能未同步到输入框视图。测试时需同时断言FormControl值和输入框value,确保两者一致。
内容的提问来源于stack exchange,提问作者szelelaci
相关产品推荐
相关产品推荐

