【问题标题】:Testing angular component which contains a custom form control?测试包含自定义表单控件的角度组件?
【发布时间】:2018-09-05 05:36:00
【问题描述】:

我有一个简单的角度组件,如下所示。

import { Component, Input, forwardRef, ViewEncapsulation } from "@angular/core";
import { NG_VALUE_ACCESSOR, ControlValueAccessor } from "@angular/forms";

export const CUSTOM_CHECKBOX_CONTROL_VALUE_ACCESSOR: any = {
    provide: NG_VALUE_ACCESSOR,
    useExisting: forwardRef(() => CheckBoxComponent),
    multi: true
};

@Component({
    selector: "checkbox",
    templateUrl: "./checkBox.component.html",
    styleUrls: ["./checkBox.component.scss"],
    providers: [CUSTOM_CHECKBOX_CONTROL_VALUE_ACCESSOR],
    encapsulation: ViewEncapsulation.None
})
export class CheckBoxComponent implements ControlValueAccessor {
    @Input() name: string;
    // Placeholders for the callbacks which are later provided
    // by the Control Value Accessor
    private innerValue: any = "";
    private onTouchedCallback: () => void = () => { };
    private onChangeCallback: (_: any) => void = () => { };

    // get and set accessor----------------------
    get value(): any {
        return this.innerValue;
    }
    set value(v: any) {
        if (v !== this.innerValue) {
            this.innerValue = v;
            this.onChangeCallback(v);
        }
    }

    // From ControlValueAccessor interfaces--------------
    writeValue(value: any): void {
        if (value !== this.innerValue) {
            this.innerValue = value;
        }
    }
    registerOnChange(fn: any): void { this.onChangeCallback = fn; }
    registerOnTouched(fn: any): void { this.onTouchedCallback = fn; }
}

我正在编写一个单元测试。我的测试如下所示

    import { CheckBoxComponent, CUSTOM_CHECKBOX_CONTROL_VALUE_ACCESSOR } from "./checkBox.component";
        import { TestBed, async, ComponentFixture, fakeAsync, tick } from "@angular/core/testing";
        import { DebugElement, Component } from "@angular/core";
        import { By } from "@angular/platform-browser";
        import { FormsModule } from "@angular/forms";
        import { CommonModule } from "@angular/common";

        @Component({
            selector: "tac-checkbox",
            template: `<checkbox name="checkboxId1" label="Checkbox Label" [(ngModel)] = "checkValue" > `,
        })
        class CustomTestClass {
            checkValue = false;
        }

        describe("Component: CheckBoxComponent", () => {
            let component: CheckBoxComponent;
            let fixture: ComponentFixture<CheckBoxComponent>;
            let testComponent: CustomTestClass;
            let testFixture: ComponentFixture<CustomTestClass>;

            beforeEach(async(() => {
                TestBed.configureTestingModule({
                    imports: [FormsModule, CommonModule],
                    declarations: [CheckBoxComponent, CustomTestClass],
                    providers: [CUSTOM_CHECKBOX_CONTROL_VALUE_ACCESSOR],
                }).compileComponents().then(() => {
                    fixture = TestBed.createComponent(CheckBoxComponent);
                    component = fixture.componentInstance;
                    component.name = "checkbox";
                });
            }));

            it("...", fakeAsync(() => {
                testFixture = TestBed.createComponent(CustomTestClass);
                testComponent = testFixture.componentInstance;

                fixture.detectChanges();
                const onChangeEvent = (change: any) => { console.log("There were changes ", change); };
                const registerOnChangeMock = spyOn(component, "registerOnChange").and.callThrough();
                const registerOnTouchedMock = spyOn(component, "registerOnTouched").and.callThrough();
                const onMockWriteValue = spyOn(component, "writeValue").and.callThrough();

                component.registerOnChange(onChangeEvent);

                component.registerOnTouched(onChangeEvent);

                testComponent.checkValue = !testComponent.checkValue;
                fixture.detectChanges();
                testFixture.detectChanges();
                tick();
                fixture.whenStable().then(() => {
                    expect(registerOnChangeMock).toHaveBeenCalledTimes(1); //pass
                    expect(registerOnTouchedMock).toHaveBeenCalledTimes(1);
 //pass
                    expect(onMockWriteValue).toHaveBeenCalled(); //failed
                    expect(testComponent.checkValue).toEqual(component.value); //failed
                });
            }));

        });

根据我上面的测试,我希望 component.value 等于 testComponent.value。然而这失败了,我的问题是考虑我改变了 testComponent 值,不应该更新控制值吗?我也期望每当我更改 testComponent 值时都会调用writeValue,但事实并非如此。请问我在做什么错?任何帮助将不胜感激。

【问题讨论】:

    标签: angular unit-testing karma-runner


    【解决方案1】:

    这是因为component 与您期望的实例不同。在beforeEach 中,您正在创建新的CheckBoxComponent,在it 中,您正在创建测试主机组件CustomTestClass 的实例。 Angular 创建新的CheckBoxComponent 作为测试主机组件的子组件,因此当您更改主机组件的输入值时,它不会反映在component 实例中。

    您应该做的是仅创建测试主机组件,并在该组件中注入 CheckBoxComponent@ViewChild 并在测试中使用该实例。

    代码:

    删除.then 中的beforeEach 部分。

    编辑您的测试主机组件:

    class CustomTestClass {
       checkValue = false;
       @ViewChild(CheckBoxComponent) checkBoxComponent: CheckBoxComponent;
    }
    

    在您的测试中将component 替换为testComponent.checkBoxComponent

    【讨论】:

      猜你喜欢
      • 2018-11-22
      • 2018-09-03
      • 2011-03-15
      • 2020-05-02
      • 2021-09-26
      • 2021-03-15
      • 2011-02-26
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多