it-swarm.com.de

Angular 2: Wie man ChangeDetectorRef beim Unit-Test verspottet

Ich habe gerade mit Unit-Testing begonnen und konnte mich über meine eigenen Dienste und einige von Angular und Ionic lustig machen, aber egal was ich tue, ChangeDetectorRef bleibt gleich.

Ich meine, welche Art von Zauberei ist das?

beforeEach(async(() => 
    TestBed.configureTestingModule({
      declarations: [MyComponent],
      providers: [
        Form, DomController, ToastController, AlertController,
        PopoverController,

        {provide: Platform, useClass: PlatformMock},
        {
          provide: NavParams,
          useValue: new NavParams({data: new PageData().Data})
        },
        {provide: ChangeDetectorRef, useClass: ChangeDetectorRefMock}

      ],
      imports: [
        FormsModule,
        ReactiveFormsModule,
        IonicModule
      ],
    })
    .overrideComponent(MyComponent, {
      set: {
        providers: [
          {provide: ChangeDetectorRef, useClass: ChangeDetectorRefMock},
        ],
        viewProviders: [
          {provide: ChangeDetectorRef, useClass: ChangeDetectorRefMock},
        ]
      }
    })
    .compileComponents()
    .then(() => {
      let fixture = TestBed.createComponent(MyComponent);
      let cmp = fixture.debugElement.componentInstance;

      let cdRef = fixture.debugElement.injector.get(ChangeDetectorRef);

      console.log(cdRef); // logs ChangeDetectorRefMock
      console.log(cmp.cdRef); // logs ChangeDetectorRef , why ??
    })
  ));

 it('fails no matter what', async(() => {
    spyOn(cdRef, 'markForCheck');
    spyOn(cmp.cdRef, 'markForCheck');

    cmp.ngOnInit();

    expect(cdRef.markForCheck).toHaveBeenCalled();  // fail, why ??
    expect(cmp.cdRef.markForCheck).toHaveBeenCalled(); // success

    console.log(cdRef); // logs ChangeDetectorRefMock
    console.log(cmp.cdRef); // logs ChangeDetectorRef , why ??
  }));

@Component({
  ...
})
export class MyComponent {
 constructor(private cdRef: ChangeDetectorRef){}

 ngOnInit() {
   // do something
   this.cdRef.markForCheck();
 }
}

Ich habe alles versucht, async, fakeAsync, injector([ChangeDetectorRef], () => {}).

Nichts funktioniert.

10
Ankit Singh

Für den Fall, dass jemand darauf stößt, ist dies eine Möglichkeit, die für mich gut funktioniert hat:

Während Sie die ChangeDetectorRef-Instanz in Ihren Konstruktor einfügen:

 constructor(private cdRef: ChangeDetectorRef) { }

Sie haben diese cdRef als eines der privaten Attribute für die Komponente. Das bedeutet, Sie können die Komponente ausspionieren, dieses Attribut entfernen und es zurückgeben lassen, was Sie wollen. Außerdem können Sie seine Aufrufe und Parameter nach Bedarf aktivieren.

Rufen Sie in Ihrer Spezifikationsdatei Ihr TestBed auf, ohne das ChangeDetectorRef anzugeben, da es nicht das bereitstellt, was Sie ihm geben. Stellen Sie die Komponente auf denselben Wert vor jedem Block ein, damit sie zwischen den Spezifikationen zurückgesetzt wird, wie in den Dokumenten hier :

component = fixture.componentInstance;

Spionieren Sie dann in den Tests direkt das Attribut aus

describe('someMethod()', () => {
  it('calls detect changes', () => {
    const spy = spyOn((component as any).cdRef, 'detectChanges');
    component.someMethod();

    expect(spy).toHaveBeenCalled();
  });
});

Mit dem Spion können Sie .and.returnValue() verwenden und alles zurückgeben lassen, was Sie brauchen.

Beachten Sie, dass (component as any) als cdRef verwendet wird und ein privates Attribut ist. Aber privat existiert nicht im aktuell kompilierten Javascript, so dass darauf zugegriffen werden kann.

Es liegt an Ihnen, auf diese Weise zur Laufzeit auf private Attribute für Ihre Tests zuzugreifen. Ich persönlich habe kein Problem damit, ich mache es auf meine Spezifikationen, um mehr Abdeckung zu bekommen.

8
Juan

Ich bin mir nicht sicher, ob dies eine neue Sache ist oder nicht, aber auf changeDetectorRef kann über ein Fixture zugegriffen werden.

Siehe Dokumente: https://angular.io/guide/testing#componentfixture-properties

Wir sind auf dasselbe Problem mit dem Verspotten des Änderungsdetektors gestoßen, und dies ist letztendlich die Lösung

1
FDIM

Wenn Sie beim Komponententest ChangeDetectorRef verspotten, um die Abhängigkeitsinjektion für eine zu erstellende Komponente zu erfüllen, können Sie einen beliebigen Wert übergeben.

Für meinen Fall habe ich folgendes gemacht:

TestBed.configureTestingModule({
  providers: [
    FormBuilder,
    MyComponent,
    { provide: ChangeDetectorRef, useValue: {} }
  ]
}).compileComponents()
injector = getTestBed()
myComponent = injector.get(MyComponent)

Es wird myComponent erfolgreich erstellt. Stellen Sie einfach sicher, dass für den Testausführungspfad keine ChangeDetectorRef erforderlich ist. Wenn Sie dies tun, ersetzen Sie useValue: {} durch ein geeignetes Scheinobjekt.

In meinem Fall musste ich nur ein paar Dinge zur Formularerstellung mit FormBuilder testen.

0
kctang

Wahrscheinlich ist ein Punkt, auf den hingewiesen werden muss, dass Sie hier im Wesentlichen Ihren eigenen Code testen möchten und nicht den Änderungsdetektor selbst (der vom Angular -Team getestet wurde). Meiner Meinung nach ist dies ein guter Indikator dafür, dass Sie den Aufruf an den Änderungsdetektor in eine lokale private Methode extrahieren sollten (privat, da Sie keinen Komponententest durchführen möchten), z.

private detectChanges(): void {
    this.cdRef.detectChanges();
}

In Ihrem Komponententest möchten Sie dann überprüfen, ob Ihr Code diese Funktion und damit die Methode aus dem ChangeDetectorRef aufgerufen hat. Zum Beispiel:

it('should call the change detector',
    () => {
        const spyCDR = spyOn((cmp as any), 'detectChanges' as any);
        cmp.ngOnInit();
        expect(spyCDR).toHaveBeenCalled();
    }
);

Ich hatte genau die gleiche Situation, und dies wurde mir als allgemeine bewährte Methode für Komponententests von einem erfahrenen Entwickler vorgeschlagen, der mir sagte, dass Komponententests Sie nach diesem Muster dazu zwingen, Ihren Code besser zu strukturieren. Mit der vorgeschlagenen Umstrukturierung stellen Sie sicher, dass sich Ihr Code flexibel ändern lässt, z. Wenn Angular die Art und Weise ändert, in der sie uns Änderungserkennung bieten, müssen Sie nur die detectChanges-Methode anpassen.

0
ArthurT