• Najnowsze pytania
  • Bez odpowiedzi
  • Zadaj pytanie
  • Kategorie
  • Tagi
  • Zdobyte punkty
  • Ekipa ninja
  • IRC
  • FAQ
  • Regulamin
  • Książki warte uwagi

Angular testy jednostkowe

Object Storage Arubacloud
+1 głos
717 wizyt
pytanie zadane 24 kwietnia 2020 w JavaScript przez michal_php Stary wyjadacz (13,700 p.)
edycja 24 kwietnia 2020 przez michal_php

Cześć.

Testuje swój kod i mam dwa problemy:

kod , który testuje: 

  @ViewChild('myTasksComponentRef') myTasksComponentRef: MyTasksComponent;
  @ViewChild('addTaskComponentRef') addTaskComponentRef: AddTaskComponent;

  constructor() { }

  ngAfterViewInit(): void {
    this.showAll();
  }

  showAll() {
    this.myTasksComponentRef.getVal(null);
  }

  newTask() {
    this.addTaskComponentRef.closerManager(true);
  }

Test tego kodu:

describe('ProjectComponent', () => {
  let component: ProjectComponent;
  let fixture: ComponentFixture<ProjectComponent>;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [
        ProjectComponent,
      ]
    })
    .compileComponents();
  }));

  beforeEach(() => {
    fixture = TestBed.createComponent(ProjectComponent);

    component = fixture.componentInstance;
  });

  it('should create', () => {
     expect(component).toBeTruthy(); // doc here we check a successful create a object of component
   });

  it('should test button correctly design', () => {
    const button = fixture.debugElement.query(By.css('button')).nativeElement;
    expect(button).toBeTruthy();
    expect(button.textContent).toContain('Nowy projekt');
  });

  it('should test button correctly works', () => {
   // const form = fixture.debugElement.query(By.css('form')).nativeElement;
    const button = fixture.debugElement.query(By.css('button')).nativeElement;
    button.click(true);
    //fixture.detectChanges();  tutaj jest perwszy błąd
    //expect(form.textContent).toBeTruthy();
  });
});

1) błąd jak od komentuje w teście fixture.detectChanges() to dostaje błąd ,że myTasksComponentRef nie jest funkcją  :

ProjectComponent should test button correctly works FAILED
        TypeError: this.myTasksComponentRef.getVal is not a function

2) to chciałem zrobić aby przetestować czy po kliknięciu buttona otwiera się nowa karta posiadająca form ,ale za chiny ciągle wywala mi ,że nie może pobrać takiej wartości z nieistniejącego elementu.

Z góry bardzo dziękuje za pomoc i wszelkie uwagi.

1 odpowiedź

0 głosów
odpowiedź 24 kwietnia 2020 przez ScriptyChris Mędrzec (190,190 p.)

Sprawdź, czy w teście prawidłowo utworzył się property myTaskComponentRef. Możliwe, że musisz zamockować MyTaskComponenthttps://medium.com/@abdul_74410/towards-better-testing-in-angular-part-1-mocking-child-components-b51e1fd571da#331e

Pokaż jeszcze kod HTML. Skoro pobierasz znacznik komponentu MyTaskComponent, to selektor "myTaskComponentRef" nie jest poprawny (chyba, że dodałeś referencję do tego znacznika w HTMLu).


//expect(form.textContent).toBeTruthy();

Jeśli form należy do child componentu, a robisz unity dla parent componentu, to w tym teście mieszasz odpowiedzialność. Jeśli więc formularz należy do childa, to napisz osobny unit test (mam na myśli całkiem osobny test/plik) dla childa, a przy testowaniu, czy przycisk parenta zachowuje się prawidłowo sprawdź co zmienia się z punktu widzenia parenta i ewentualnie, czy są wołane jakieś metody childa (ale już nie testuj tutaj co robią, bo to nie jest odpowiedzialność parenta).

komentarz 24 kwietnia 2020 przez michal_php Stary wyjadacz (13,700 p.)

Tutaj daje kod html :

<div class="card mt-5 area__div">
  <div class="card-header area__head text-left">
    <h5>Twoje projekty
      <button (click)="newTask()" class="btn btn-outline-success btn-sm float-right">Nowy projekt</button>
    </h5>
  </div>

  <app-my-tasks #myTasksComponentRef></app-my-tasks>

</div>
<app-add-task #addTaskComponentRef></app-add-task>

Dobra to już kumam 2 swój problem.

Nawiązując do 1) problemu .

Jak mam sprawdzić w senie czy poprawnie się buduje obiekt myTaskComponentRef.?

komentarz 24 kwietnia 2020 przez ScriptyChris Mędrzec (190,190 p.)

Jeśli dostajesz błąd, że getVal nie jest funkcją

TypeError: this.myTasksComponentRef.getVal is not a function

, to sprawdź (wypisując np. w konsoli) czy component.myTasksComponentRef zawiera property getVal (zamiast this podstaw zmienną component, bo ona zawiera instancję parent componentu).

komentarz 24 kwietnia 2020 przez michal_php Stary wyjadacz (13,700 p.)

Dobra zaczyna chyba już łapać powoli.

Miałbym jeszcze jedno pytanie. Otóż mam już napisany test do innego kodu i zwraca coś czego nie kumam 

test:

class TaskMockService implements Partial<TaskService> {
  getAllTask() {return null; }
}

describe('MyTasksComponent', () => {
  let component: MyTasksComponent;
  let fixture: ComponentFixture<MyTasksComponent>;
  let taskService: TaskMockService;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [
        MyTasksComponent
      ], providers: [
        {provide: TaskService, useClass: TaskMockService}
      ]
    })
    .compileComponents();
  }));

  describe('getAllTaskUsers', () => {
      beforeEach( () => {

        taskService = TestBed.inject(TaskService);

        spyOn(taskService, 'getAllTask').and.returnValue(of([{task: 'task'}]));

      });

      it('should get getAllTask', () => {
        expect(taskService.getAllTask).toHaveBeenCalled();
    });

  
  });


});

i component :

 @ViewChild('showTaskComponentRef') showTaskComponentRef: ShowTaskComponent;
  @ViewChild('transferTaskComponentRef') transferTaskComponentRef: TransferTaskComponent;

  tasks: Task[];
  searchName: string;
  idUser: string;
  load = true;

  constructor(
    private taskService: TaskService,
    private projectService: ProjectService
  ) {
    this.projectService.$data.subscribe(val => {
      this.putTable(val);
    });
  }

  getAllTaskUsers() {
    this.taskService.getAllTask(this.idUser).pipe().subscribe(task => {
      this.tasks = task;
      this.load = false;
    });
  }

I błąd , którego nie rozumiem :

MyTasksComponent getAllTaskUsers should get getAllTask FAILED
        Error: Expected spy getAllTask to have been called.

Czy to jest spowodowane tym ,że moja tablica zawiera tylko samo 'task' bez innych wartości czy coś innego przeoczyłem ?

komentarz 24 kwietnia 2020 przez ScriptyChris Mędrzec (190,190 p.)

Wygląda na to, że test nie przeszedł, ponieważ oczekujesz że taskService.getAllTask będzie wywołane, ale nigdzie go nie wywołujesz.

To, że w 26 linii testu ustawiasz szpiega, który sprawi że metoda zwróci wymuszoną wartość to jedno, ale musisz jeszcze tą metodę gdzieś wywołać, żeby expect..toHaveBeenCalled() zostało spełnione.

https://jasmine.github.io/2.0/introduction#section-Spies:_%3Ccode%3Eand.returnValue%3C/code%3E

komentarz 24 kwietnia 2020 przez michal_php Stary wyjadacz (13,700 p.)

Bardzo fajnie przedstawiony kod, dziękuje. Ale to w , którym miejscu mam dokładnie wywołać taskService.getAllTask ?

komentarz 24 kwietnia 2020 przez ScriptyChris Mędrzec (190,190 p.)

W Twoim kodzie metodę taskService.getAllTask wywołuje metoda getAllTaskUsers z componentu MyTaskComponent. Nie pokazałeś natomiast kodu, który woła tą metodę...

Jeśli testujesz MyTaskComponent, to z jego perspektywy metoda getAllTaskUsers woła taskService.getAllTask, otrzymuje task jako parametr subskrypcji oraz przestawia flagę. Więc przypadku tego testu możesz zawołać metodę componentu getAllTaskUsers, sprawdzić że została wywołana (ten expect, który już masz) oraz sprawdzić, czy do callbacka subskrypcji został przekazany parametr task property tasks zawiera prawidłową wartość (u Ciebie to będzie po prostu string "task" zwrócony ze Spy'a) oraz czy flaga loaded została przestawiona na false.

komentarz 25 kwietnia 2020 przez michal_php Stary wyjadacz (13,700 p.)

Dobra nie radze sobie z tymi testami. Jeśli mógłbym Cie prosić o pomoc byłbym niesamowicie Ci wdzięczny.

Otóż nie radze sobie z tym pierwszym testem. Z myTaskComponentRef.

Mój kod:

import {AfterViewInit, Component, ViewChild} from '@angular/core';
import {MyTasksComponent} from './my-tasks/my-tasks.component';
import {AddTaskComponent} from './add-task/add-task.component';

@Component({
  selector: 'app-project',
  templateUrl: './project.component.html',
  styleUrls: ['./project.component.less']
})
export class ProjectComponent implements AfterViewInit {
  @ViewChild('myTasksComponentRef') myTasksComponentRef: MyTasksComponent;
  @ViewChild('addTaskComponentRef') addTaskComponentRef: AddTaskComponent;

  constructor() { }

  ngAfterViewInit(): void {
    this.showAll();
  }

  showAll() {
    this.myTasksComponentRef.getVal(null);
  }

  newTask(bol: boolean) {
    this.addTaskComponentRef.closerManager(bol);
  }

}

html do niego:

<div class="card mt-5 area__div">
  <div class="card-header area__head text-left">
    <h5>Twoje projekty
      <button (click)="newTask(true)" class="btn btn-outline-success btn-sm float-right">Nowy projekt</button>
    </h5>
  </div>

  <app-my-tasks #myTasksComponentRef></app-my-tasks>

</div>
<app-add-task #addTaskComponentRef></app-add-task>

I test do niego:

import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { ProjectComponent } from './project.component';
import {By} from '@angular/platform-browser';
import { MyTasksComponent } from './my-tasks/my-tasks.component';
import {HttpClient} from '@angular/common/http';

describe('ProjectComponent', () => {
  let component: ProjectComponent;
  let fixture: ComponentFixture<ProjectComponent>;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [
        ProjectComponent,
        MyTasksComponent
      ], /*providers: [
        {provide: http, useClass: HttpClientMock}
      ]*/
    })
    .compileComponents();
  }));

  beforeEach(() => {
    fixture = TestBed.createComponent(ProjectComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('should create', () => {
     expect(component).toBeTruthy(); // doc here we check a successful create a object of component
   });

  it('should test button correctly design', () => {
    const button = fixture.debugElement.query(By.css('button')).nativeElement;
    expect(button).toBeTruthy();
    expect(button.textContent).toContain('Nowy projekt');
  });

// nowy test
  it('should call update myTasksComponentRef', () => {
    spyOn(component.myTasksComponentRef,  'getVal');
    component.showAll();
    expect(component.myTasksComponentRef.getVal).toHaveBeenCalled();
  });
});

Zrobiłem tak jak mi radziłeś, abym w console.log wyświetlił sobie component.myTasksComponentRef. I faktycznie wywala mi undefinited. Jak z kolei dodałem ten nowy test to teraz mi pokazuje ,że nie mam providers dla http.

Tak wygląda klasa dziecka MyTaskComponent:

  @Component({
  selector: 'app-my-tasks',
  templateUrl: './my-tasks.component.html',
  styleUrls: ['./my-tasks.component.less']
})
export class MyTasksComponent {
  @ViewChild('showTaskComponentRef') showTaskComponentRef: ShowTaskComponent;
  @ViewChild('transferTaskComponentRef') transferTaskComponentRef: TransferTaskComponent;

  tasks: Task[];
  searchName: string;
  idUser: string;
  load = true;

  constructor(
    private taskService: TaskService,
    private projectService: ProjectService
  ) {
    this.projectService.$data.subscribe(val => {
      this.putTable(val);
    });
  }

  getAllTaskUsers() {
    this.taskService.getAllTask(this.idUser).pipe().subscribe(task => {
      this.tasks = task;
      this.load = false;
    });
  }

  getVal(idUser: string) {
    this.idUser = idUser;
    this.getAllTaskUsers();
  }

I przyznam szczerze ,że nie za bardzo kumam to wszystko.

Jak mam teraz zrobić aby dobrze przetestować ponieważ ProjectComponent prowadzi do getVal() a to z kolei odwołuje się do innej metody , która pobiera z service. Jak mogę sensownie zastosować tutaj mock lub suba ? 

komentarz 26 kwietnia 2020 przez ScriptyChris Mędrzec (190,190 p.)

Jak z kolei dodałem ten nowy test to teraz mi pokazuje ,że nie mam providers dla http.

A dodałeś go w TestBed.configureTestingModule? Bo w liniach 16-18 to jest zakomentowane.

Zrobiłem tak jak mi radziłeś, abym w console.log wyświetlił sobie component.myTasksComponentRef. I faktycznie wywala mi undefinited.

+

Jak mam teraz zrobić aby dobrze przetestować ponieważ ProjectComponent prowadzi do getVal() a to z kolei odwołuje się do innej metody , która pobiera z service. Jak mogę sensownie zastosować tutaj mock lub suba ?

Artykuł o testowaniu parent componentu, który korzysta z child componentu przez @ViewChild:

https://medium.com/angular-in-depth/angular-unit-testing-viewchild-4525e0c7b756

Podobne pytania

0 głosów
1 odpowiedź 343 wizyt
pytanie zadane 17 kwietnia 2020 w JavaScript przez michal_php Stary wyjadacz (13,700 p.)
0 głosów
0 odpowiedzi 93 wizyt
pytanie zadane 6 lipca 2020 w JavaScript przez michal_php Stary wyjadacz (13,700 p.)
+1 głos
1 odpowiedź 259 wizyt

92,576 zapytań

141,426 odpowiedzi

319,652 komentarzy

61,961 pasjonatów

Motyw:

Akcja Pajacyk

Pajacyk od wielu lat dożywia dzieci. Pomóż klikając w zielony brzuszek na stronie. Dziękujemy! ♡

Oto polecana książka warta uwagi.
Pełną listę książek znajdziesz tutaj.

Akademia Sekuraka

Kolejna edycja największej imprezy hakerskiej w Polsce, czyli Mega Sekurak Hacking Party odbędzie się już 20 maja 2024r. Z tej okazji mamy dla Was kod: pasjamshp - jeżeli wpiszecie go w koszyku, to wówczas otrzymacie 40% zniżki na bilet w wersji standard!

Więcej informacji na temat imprezy znajdziecie tutaj. Dziękujemy ekipie Sekuraka za taką fajną zniżkę dla wszystkich Pasjonatów!

Akademia Sekuraka

Niedawno wystartował dodruk tej świetnej, rozchwytywanej książki (około 940 stron). Mamy dla Was kod: pasja (wpiszcie go w koszyku), dzięki któremu otrzymujemy 10% zniżki - dziękujemy zaprzyjaźnionej ekipie Sekuraka za taki bonus dla Pasjonatów! Książka to pierwszy tom z serii o ITsec, który łagodnie wprowadzi w świat bezpieczeństwa IT każdą osobę - warto, polecamy!

...