Testowanie Angularów nie musi być koszamrem

Lessons learned z testowania aplikacji w Angularze po półtorarocznym projekcie i napisaniu dwóch bibliotek open source.

Speaker

Wojtek Przechodzeń

Testowanie Angularów nie musi być koszamrem [PL]

2018-01-10

@wprzechodzen

Agenda

  1. Problemy, kóre możemy napotkać testując Angulary
  2. Prosty przepis jak sobie z nimi poradzić

Hand voting time!

  1. Kto z Was developuje aplikację w AngularJS

Antypatterny

Testowanie szczegółów implementacyjnych

            
            it('bad test', function() {
                var $scope = {};
                var controller = $controller('PasswordController',
                                             { $scope: $scope });
                $scope.password = 'longerthaneightchars';

                $scope.grade();

                expect($scope.strength).toEqual('strong');
            });
            
        
            
            it('should display a different test title', () => {
                fixture = TestBed.createComponent(BannerComponent);
                comp = fixture.componentInstance;
                comp.title = 'Test Title';

                fixture.detectChanges();

                expect(el.textContent).toContain('Test Title');
            });
            
        

Zaszumione testy

            
            it('Replaces the element with the appropriate content', function() {
                var element = $compile('< a-great-eye>< /a-great-eye>')($rootScope);

                $rootScope.$digest();

                expect(element.html()).toContain("lidless, wreathed in flame, 2 times");
            });
            
        
            
            it('should keep input and h2 in sync', fakeAsync(() => {
                const fixture = TestBed.createComponent(AppComponent);
                fixture.detectChanges();
                const inputDe = fixture.debugElement.query(By.css('input[name="title"]'));
                const inputEl = inputDe.nativeElement;
                inputEl.value = 'Updated Task 1';
                inputEl.dispatchEvent(new Event('input'));
                tick();
                fixture.detectChanges();
                const de = fixture.debugElement.query(By.css('h2'));
                expect(de.nativeElement.textContent).toEqual('Updated Task 1');
            }));
            
        

Słabe testowanie komunikacji http

            
            it('should fetch authentication token', function() {
             $httpBackend.expectGET('/auth.py');
             var controller = createController();
             $httpBackend.flush();
           });
            
        
            
              mockBackend.connections.subscribe(
                (connection: MockConnection) => {
                  expect(connection.request.method).toBe(RequestMethod.Get);
                  expect(connection.request.url).toBe(expectedUrl);
                  connection.mockRespond(new Response(
                    new ResponseOptions({ body: mockResponse })
                  ));
                });
            
        

Słabe wsparcie do testowania interakcji z DOM

            
            it('should keep input and h2 in sync', fakeAsync(() => {
                const fixture = TestBed.createComponent(AppComponent);
                fixture.detectChanges();
                const inputDe = fixture.debugElement.query(By.css('input[name="title"]'));
                const inputEl = inputDe.nativeElement;
                inputEl.value = 'Updated Task 1';
                inputEl.dispatchEvent(new Event('input'));
                tick();
                fixture.detectChanges();
                const de = fixture.debugElement.query(By.css('h2'));
                expect(de.nativeElement.textContent).toEqual('Updated Task 1');
            }));
            
        

angular-test-runner

ng-test-runner

setup testów

            
            const test = require('angular-test-runner');
            const {click, expectElement} = test.actions;

            describe('counter component', () => {
              let app, server;

              beforeEach(() => {
                app = test.app(['counterApp']);
              });
                ...testy
            });
            
        
            
            import test, {App, click, expectThat} from "ng-test-runner";
            import {CounterComponent, CounterModule} from './counter';

            describe('Counter Component', () => {
                let app: App;

                beforeEach(() => {
                    app = test(CounterModule);
                });
                ...testy
            });
            
        

przykładowe testy

            
            it('should increment counter value by 1', () => {
              const html = app.runHtml('< counter value="start">< /counter>', {start: 0});

              html.perform(
                click.in('button#increment')
              );

              html.verify(
                expectElement('#counter').toHaveText('1')
              );
            });
            
        
            
            it('should increment counter value by 1', () => {
                const comp = app.run(CounterComponent, {value: 0});

                comp.perform(
                    click.in('button.increment')
                );

                comp.verify(
                    expectThat.textOf('.counter').isEqualTo('1')
                );
            });
            
        

komunikacja http

            
            it('should increment counter value by 1', () => {
            const html = app.runHtml('< counter value="start">< /counter>', {start: 0});
            server.post('/counter/increment', (req) => req.sendStatus(200));

            html.perform(
              click.in('button#increment')
            );

            html.verify(
              expectElement('#counter').toHaveText('1')
            );
          });
            
        
            
            it('responds with specific code', function () {
            const server = http(), app = test(MyModule);
            server.get('/greeting', req => {
                req.sendJson({message: 'Hello from server!'});
            });

           const comp = app.run(MyComponent);

            comp.verify(
                expectThat.textOf('.greeting').isEqualTo('Hello from server!')
            );
        });
            
        

Linki do terminów użytych w prezentacji

  1. angular-test-runner
  2. ng-test-runner
  3. Testowanie tautologiczne

See you next month at WarsawJS