Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Testgetriebene Entwicklung von Angular Apps (WJ...

Martin Maier
November 06, 2018

Testgetriebene Entwicklung von Angular Apps (WJAX 2018)

In der Präsentation stellen wir einen Ansatz vor, wie Angular Anwendungen sehr gut testgetrieben entwickelt werden können. Dabei gehe ich auf die verschiedenen Testarten ein und wie innerhalb eines agilen Prozesses eine Story testgetrieben umgesetzt werden kann. Anschließend demonstriere ich den Entwicklungsprozess anhand eines kleinen Demo-Projekts.
Das Projekt zur Präsentation liegt auf GitHub (https://github.com/maimArt/tddangular). Dort sind die einzelnen Schritte innerhalb der Commits und des GitHub Projects dokumentiert.

Martin Maier

November 06, 2018
Tweet

Other Decks in Programming

Transcript

  1. IHR KONTAKT Twitter: E-Mail: sogehtsoftware.de Z Twitter: E-Mail: Z Martin

    Maier Senior Consultant Softwareentwicklung @_maimArt_ [email protected] Manuel Mauky Lead Developer @manuel_mauky [email protected]
  2. Seite sogehtsoftware.de Agenda TDD ANGULAR 3 1. Testdriven Development 2.

    Testarten 3. Entwicklungsprozess 4. Coding (TODO-Liste) 5. Systemintegrationstests
  3. Seite sogehtsoftware.de › Was versteht man unter testgetriebener Entwicklung? ›

    TDD Zyklus TEST DRIVEN DEVELOPMENT Bringe Unit Test zum laufen Refactor Schreibe Unit Test
  4. Seite sogehtsoftware.de 6 › Unzufrieden mit › Codequalität › Testabdeckung

    › Mit TDD erreichen wir › hohe Testabdeckung › gut designter, lesbaren Code › Entwicklung fokussieren › hohen Synergieeffekt mit Pair-Programming TEST DRIVEN DEVELOPMENT Warum TDD?
  5. Seite sogehtsoftware.de 9 TESTARTEN Testgrenzen Modul Angular App Komponente Service

    Komponente Modul Modul Mock externes System Browser Komponententest Schnittstellen Angular-Integrationstests System-Integrationstests
  6. Seite sogehtsoftware.de Agiler Entwicklungsprozess ENTWICKLUNGSPROZESS PLANNING REVIEW SPRINT BACKLOG PRODUCT

    BACKLOG SPRINT Product Owner Inkrement Team lauffähiger Produktbestandteil Ergebnisse des Planning-Meetings n-tägige Iterationsschleife Backlog Grooming
  7. Seite sogehtsoftware.de 12 Testgetriebene Story ENTWICKLUNGSPROZESS neue Story Spezifizierte Akzeptanz-

    kriterien Skelett der E2E-Tests E2E erfolgreich Schreibe E2E-Test Bringe Unit Test zum laufen Refactor Schreibe Unit Test Erfolgreiche E2E-Tests fertige Story Erfüllte Akzeptanz- kriterien
  8. Seite sogehtsoftware.de 16 › GitHub https://github.com/maimArt/tddangular › Entwicklungsschritte dokumentiert in

    „Projects“ und „Commits“ › Startpunkt: Nach Sprint 0 (Commit 4) GitHub TODO LISTE
  9. Seite sogehtsoftware.de 17 › ng new tddangular --p todo ›

    Karma.config → ChromeHeadless › Protractor config: › Headless Moduls aktiviert › Plugin „protractor-console“ installiert und aktiviert › Mock Server › json-server installiert › 3 Todos als Mock-Daten angelegt › Hilfsklasse MockServer: start(),stop(); › npm run start-mock-server in package.json › environment.todosUrl → Url zum MockServer › E2e Spec und PageObject angelegt Sprint 0 - Projekt Setup TODO LISTE
  10. Seite sogehtsoftware.de 18 todo-list.e2e-spec.ts Story Kickoff -> E2E-Skelett STORY „ZEIGE

    LISTE VON TODOS " xit('should display a header "TODOs"', () => { }); xit('should display a list of TODOs', function () { }); xit('should display TODOs of the backend', function () { }); E2E Unit
  11. Seite sogehtsoftware.de 19 todo-list.e2e-spec.ts Die Liste ist durch die Überschrift

    „TODOs“ erkennbar STORY „ZEIGE LISTE VON TODOS " getHeaderText(): Promise<string> { return element(by.css('todo-list h1')).getText(); } todo-list.po.ts it('should display a header "TODOs"', () => { expect(todoList.getHeaderText()).toEqual('TODOs'); }); E2E Unit
  12. Seite sogehtsoftware.de 20 list.component.spec.ts Die Liste ist durch die Überschrift

    „TODOs“ erkennbar STORY „ZEIGE LISTE VON TODOS " E2E Unit it('should display a header TODOs', function () { const headerElement: HTMLHeadElement = fixture.nativeElement.querySelector('h1'); expect(headerElement.textContent).toEqual('TODOs') }); list-component.html <h1>TODOs</h1> E2E Unit
  13. Seite sogehtsoftware.de 21 todo-list.e2e-spec.ts Ich sehe eine Liste von TODOs

    STORY „ZEIGE LISTE VON TODOS " todo-list.po.ts E2E Unit it('should display a list of TODOs', function () { expect(todoList.isTodoListShown()).toBe(true); }); isTodoListShown(): Promise<boolean> { return element(by.css('todo-list ul')).isPresent(); }
  14. Seite sogehtsoftware.de 22 list.component.spec.ts Ich sehe eine Liste von TODOs

    STORY „ZEIGE LISTE VON TODOS " E2E Unit list.component.html E2E Unit it('should display a todo list', function () { const todoListElement: HTMLUListElement = fixture.nativeElement.querySelector('ul'); expect(todoListElement).toBeTruthy(); }); <h1>TODOs</h1> <ul></ul>
  15. Seite sogehtsoftware.de 23 todo-list.e2e-spec.ts Die TODOs wurden aus dem Fremdsystem

    geladen STORY „ZEIGE LISTE VON TODOS " todo-list.po.ts E2E Unit it('should display TODOs of the backend', function () { expect(todoList.getTodoTexts()).toEqual(['Make coffee', 'Start coding', 'Make some coffee again']) }); getTodoTexts(): Promise<string[]> { return element.all(by.css('todo-list ul li')).map(listItem => listItem.getText()); }
  16. Seite sogehtsoftware.de 24 todo.service.spec.ts Die TODOs wurden aus dem Fremdsystem

    geladen STORY „ZEIGE LISTE VON TODOS " E2E Unit todo.service.ts E2E Unit it('should request all todos from the backend', function () { const todosFromBackend: Todo[] = [{ id: '1', text: 'TODO1' }, { id: '2', text: 'TODO2' }]; const todos$: Observable<Todo[]> = service.getTodos(); todos$.subscribe(nextTodos => { expect(nextTodos).toEqual(todosFromBackend); }); const testRequest = http.expectOne(environment.todosUrl); expect(testRequest.request.method).toEqual('GET'); testRequest.flush(todosFromBackend); http.verify(); }); export interface Todo { id: string; text: string; } constructor(private http: HttpClient) { } getTodos(): Observable<Todo[]> { return this.http.get<Todo[]>(environment.todosUrl); } todo.type.ts
  17. Seite sogehtsoftware.de 25 list.component.spec.ts Die TODOs wurden aus dem Fremdsystem

    geladen STORY „ZEIGE LISTE VON TODOS " E2E Unit list.component.ts E2E Unit todos$: Observable<Todo[]>; constructor(private todoService: TodoService) { } ngOnInit() { this.todos$ = this.todoService.getTodos(); } it('should initially load list of todos from todo service', function (done: DoneFn) { spyOn(todoService, 'getTodos').and.callThrough(); component.ngOnInit(); fixture.detectChanges(); expect(todoService.getTodos).toHaveBeenCalled(); component.todos$.subscribe(nextTodos => { expect(nextTodos).toEqual(todoService.todos); done(); }) });
  18. Seite sogehtsoftware.de 26 list.component.spec.ts Die TODOs wurden aus dem Fremdsystem

    geladen STORY „ZEIGE LISTE VON TODOS " E2E Unit E2E Unit it('should display todos requested from todo service', function () { component.ngOnInit(); fixture.detectChanges(); const todoElements: NodeListOf<HTMLElement> = fixture.nativeElement.querySelectorAll('li'); const todoTexts: string[] = Array.from(todoElements).map(todoElement => todoElement.textContent); expect(todoTexts).toEqual(todoService.todos.map(todo => todo.text)); }); <h1>TODOs</h1> <ul> <li *ngFor="let todo of todos$ | async">{{todo.text}}</li> </ul> list.component.html
  19. Seite sogehtsoftware.de 27 app.module.ts Die TODOs wurden aus dem Fremdsystem

    geladen STORY „ZEIGE LISTE VON TODOS " E2E Unit Story @NgModule({ declarations: [ AppComponent, ListComponent ], imports: [ BrowserModule, HttpClientModule ], providers: [], bootstrap: [AppComponent] }) export class AppModule { }
  20. IHR KONTAKT Twitter: E-Mail: sogehtsoftware.de Z Twitter: E-Mail: Z Martin

    Maier Senior Consultant Softwareentwicklung @_maimArt_ [email protected] Manuel Mauky Lead Developer @manuel_mauky [email protected]