5

I have a simple angular component like below.

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; }
}

I am writing a unit test for. My test is like below

    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
                });
            }));

        });

From my test above, i expect component.value to equal testComponent.value. This failed however, My question is considering i changed the testComponent value, shouldn't the control value be updated as well? Also i was expecting the writeValue to be called whenever i changed the testComponent value, however, it wasn't. Please what am i doing wrong ? Any help would be appreciated.

1 Answer 1

3

It is because the component is not same instance as you are expecting. In beforeEach you are creating new CheckBoxComponent and in it you are creating instance of your test host component CustomTestClass. Angular creates new CheckBoxComponent as a child of your test host component, so when you change the input value for the host component it is not reflected in component instance.

What should you do is to create just the test host component and in that component inject the CheckBoxComponent with @ViewChild and use that instance in tests.

Code:

remove .then part in beforeEach.

edit your test host component:

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

replace component with testComponent.checkBoxComponent in your test.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.