Winkel: Integrationstests (flache Tests)



Wenn die Anwendung wächst oder es für uns sehr wichtig ist, dass sie bei jedem Refactoring korrekt funktioniert, beginnen wir über Unit- oder E2E-Tests nachzudenken.

Während der mehrjährigen Arbeit mit Angular - Anwendungen im Unternehmenssegment, die beim Refactoring und Erstellen neuer Funktionen viele Probleme hatte, scheinen Tests keine Zeitverschwendung zu sein, sondern der Schlüssel zur Stabilität in den Händen von Programmierern.

Als nächstes versuchen wir zu verstehen, wie die Basisanwendung auf Angular getestet wird, und berühren eine kleine Theorie.

Beginnen wir mit den Testarten, die Anwendungsentwickler durchführen können (in unserem Fall Frontend).



(Martin Fowler und Google Testing Blog)

Angular hat solche Methoden sofort einsatzbereit.

Schauen wir uns die Testpyramide am Beispiel von Angular-Anwendungen genauer an.

Unit Testing - isoliertes Testen von Klassenmethoden. Wir überprüfen die Funktionsweise der Methode, übergeben verschiedene Kombinationen von Parametern an die Eingabe und vergleichen das Ergebnis mit dem erwarteten.

Zum Beispiel haben wir eine Komponente, in der Hex-Strings in RGB konvertiert werden. Diese Informationen werden dem Benutzer angezeigt und auch an den Rest-Server gesendet.

Wenn wir uns mit Komponententests befassen, können wir für jede Methode in der Komponentenklasse viele Tests schreiben.

Wenn wir eine Methode rgbToHex (hex: string) => string haben, müssen wir zum Testen einer Methode mit einer solchen Signatur Folgendes tun: erwarten (rgbToHex ('777')). ToBe ('rgb (119, 119, 119)') .

Es scheint großartig zu sein, aber wir haben ein Problem aufgrund der großen Anzahl von Funktionen, die abgedeckt werden müssen, und wir werden zu faul, um Tests zu schreiben. Selbst wenn wir Unit-Tests für jede Methode in einer Komponente schreiben, können wir nicht die Richtigkeit ihrer gemeinsamen Arbeit garantieren.

Nachdem wir beispielsweise die Tabellenausgabekomponente erstellt und getestet haben, können wir sie in der Komponente "Rechner" aufrufen, aber versehentlich die falsche Bindung angeben. Dann fallen die Daten nicht in die Tabellen und voll funktionsfähige Komponenten funktionieren nicht richtig.

Bild

Integrationstests- Testen mehrerer Komponenten. Ab diesem Stadium beginnen wir nicht nur die Klassenmethoden zu testen, sondern auch ihre Bindung an HTML, d. H. Klicken Sie auf die Elemente innerhalb der Komponente. Flache Tests werden häufig in der Winkelnotation gefunden , bei der es sich im Wesentlichen um Integrationstests handelt.

Bei Shallow-Tests werden wir weiter unten näher darauf eingehen.

E2E -Test (End-to-End) - eine Möglichkeit, die Anwendung vollständig zu testen, um die Probleme von Komponententests zu lösen.

Mit diesem Ansatz schreiben wir Testskripte für eine vollständig gerenderte Anwendung, d. H. Alle Komponenten und Services werden zusammengesetzt, und wir reproduzieren Benutzeraktionen.
Das ist sehr cool, aber wir haben möglicherweise das Problem, Stubs dynamisch zu ändern (normalerweise ein statischer JSON-Server auf node.js). Um unterschiedliche Situationen zu spielen, benötigen wir möglicherweise andere Daten vom emulierten Server.

Beispiel: Wir haben eine Seite mit einer Liste von Benutzern. Eine der Komponenten dieses Bedienfelds ist die Paginierung. Es sollte sich bei unterschiedlichen Benutzerzahlen unterschiedlich verhalten. Wenn die Daten auf dem emulierten Server jedoch über json festgelegt werden, ist die Anzahl der Benutzer immer eins, und wir können nicht alle Fälle mit einer anderen Anzahl von Seiten überprüfen.

Infolgedessen können wir den Schluss ziehen, dass wir etwas zwischen e2e und Unit-Tests verwenden müssen, wenn wir Stubs flexibel ändern und nicht nach Methoden, sondern nach logischen Einheiten der Schnittstelle testen möchten. Hier kommt das flache Testen (Integrationstest) zum Einsatz.

Glossar:

  • Mocks sind Objekte zur Simulation von Antworten in verschiedenen Anwendungsfällen.
  • Stubs sind die dümmsten Stubs ohne Logik (man kann Martin lesen).
  • Karma ist ein Testläufer, der in Winkel eingebaut ist (oft wird empfohlen, stattdessen Scherz zu verwenden, aber heute nicht mehr darüber).
  • Jasmine ist ein Framework zur Beschreibung von Tests in Form von Spezifikationen (siehe unten).
  • spec - Testdateierweiterung (Spezifikationsbeschreibung im BDD-Stil).
  • Es ist der Name der Methoden für Tests in Jasmine.
  • xit ist der Name der Methoden, die nicht ausgeführt werden.
  • fit - Wenn die Spezifikationen Methoden mit demselben Namen enthalten, werden nur diese gestartet.

Flaches Testen für Angular sowie für andere Frameworks ist ein Unit-Test-Ansatz, bei dem eine Komponente mit einer Größe gerendert wird, die groß genug ist, damit sie als separate UI-Einheit mit eigener Funktionalität existieren kann.

Zum Beispiel haben wir eine Komponente zum Konvertieren von hex -> rgb. Wir können nur diese Komponente rendern, Stubs für verschiedene Situationen generieren, mögliche Anwendungsfälle für diese Komponente aus Sicht der Endbenutzer ausführen und den Betrieb der Komponente überprüfen.

Versuchen wir, ein Beispiel (Repository) herauszufinden.

Bereiten Sie die Klasse für den Zugriff auf die Komponenten der Komponente gemäß PageObject vor und fügen Sie dem Stammverzeichnis des Projekts einen Helfer hinzu.

Helfer - hilft bei der Suche nach Elementen in den Komponenten, die zum Rendern ausgewählt wurden. Auf diese Weise können wir das Leben erleichtern, wenn wir Angular Material verwenden: Dann erstellen Elemente vom Typ select eine Liste mit Optionen in einem separaten Block, und die Suche nach diesen Elementen kann zu Boilerplates führen, und ein Wrapper in Form eines Helfers kann helfen.

export class PageObjectBase {

  constructor(private root: HTMLDivElement) { }
  //    input  
  _inputValue(cssSelector: string, value: string) { 
    if (value) {
      this.root.querySelector<HTMLInputElement>(cssSelector).value = value;
      this.root.querySelector<HTMLInputElement>(cssSelector).dispatchEvent(new Event('input'));
    }
    else {
      return this.root.querySelector<HTMLInputElement>(cssSelector).value
    }
  }
  //          
  _buttonClick(cssSelector: string) {
    this.root.querySelector<HTMLButtonElement>(cssSelector).dispatchEvent(new Event('click'));
  }

}

PageObject ist eine beliebte automatisierte Testvorlage . Wird verwendet, um die Unterstützung für schriftliche Tests zu vereinfachen. Wenn wir die Benutzeroberfläche ändern, müssen wir die Tests nicht neu schreiben, sondern nur die Elementauswahl ändern.


export class ConverterFromHexPageObject extends PageObjectBase {

  constructor(root: HTMLDivElement) {
    super(root)
  }

  hex(text?: string) {
    return this._inputValue('.input-hex', text);
  }

  rgb(text?: string) {
    return this._inputValue('.input-rgb', text);
  }

  clear() {
    this._buttonClick('.btn-clear')
  }

  calc() {
    this._buttonClick('.btn-calc')
  }

}

und die Tests selbst:


// ,        api   
const urlToSave = 'http://localhost:4200/save-hex';


//  -   
describe('ConverterFromHexComponent', () => {
  let component: ConverterFromHexComponent;
  let fixture: ComponentFixture<ConverterFromHexComponent>;
  let page: ConverterFromHexPageObject;
  let httpTestingController: HttpTestingController;


  //      ,   
  beforeEach(async(() => {
    TestBed.configureTestingModule({
      imports: [ConverterModule, HttpClientTestingModule]
    })
      .compileComponents();
    httpTestingController = TestBed.get(HttpTestingController);
  }));


  //  ,   
  beforeEach(() => {
    fixture = TestBed.createComponent(ConverterFromHexComponent);
    component = fixture.componentInstance;
    page = new ConverterFromHexPageObject(fixture.nativeElement);
    fixture.detectChanges();
  });


  //    ,     http 
  afterEach(() => {
    httpTestingController.verify();
  });

  it('should create', () => {
    expect(component).toBeTruthy();
  });

  it('should clear', async () => {
    page.hex('112233'); //  input  
    expect(page.hex()).toBe('112233'); // ,    
    await page.clear(); //      
    fixture.detectChanges(); //    
    expect(page.hex()).toBe(''); // ,   clear 
  });

  it('should convert', async () => {
    page.hex('123123');
    expect(page.rgb()).toBe('');
    page.calc();
    const req = httpTestingController.expectOne(urlToSave);
    expect(req.request.method).toEqual('POST');
    expect(req.request.body.hex).toEqual('123123');
    req.flush({});
    await fixture.detectChanges();
    expect(page.rgb()).toBe('rgb(18, 49, 35)');
  });

  it('should convert three-digit hex', async () => {
    page.hex('567');
    expect(page.rgb()).toBe('');
    page.calc();
    const req = httpTestingController.expectOne(urlToSave);
    expect(req.request.method).toEqual('POST');
    req.flush({});
    await fixture.detectChanges();
    expect(page.rgb()).toBe('rgb(85, 102, 119)');
  });

  it('rgb should be empty when entered incorrectly hex', async () => {
    page.hex('qw123we');
    page.calc();
    const req = httpTestingController.expectNone(urlToSave);
    await fixture.detectChanges();
    expect(page.rgb()).toBe('');
  });

});

Alles scheint einfach zu sein, aber hier sind einige interessante Hinweise für Angular Shallow-Tests:

  • Stellen Sie immer sicher, dass keine nicht reservierten http-Anforderungen vorhanden sind. Dies hilft Ihnen, genau zu verstehen, ob unnötige Anforderungen für unsere Funktionalität vorliegen.
  • PageObject , .
  • json , .
  • , .
  • .
  • .
  • , session, local, cookie ( ).
  • fake.js.
  • .




Source: https://habr.com/ru/post/undefined/


All Articles