Angular: Pruebas de integración (pruebas superficiales)



Cuando la aplicación crece, o es muy importante para nosotros que funcione correctamente con cualquier refactorización, comenzamos a pensar en pruebas unitarias o e2e.

Durante varios años de trabajo con Angular, las aplicaciones en el segmento corporativo, después de haber detectado muchos problemas al refactorizar y crear nuevas funcionalidades, las pruebas no parecen una pérdida de tiempo, sino la clave para la estabilidad en manos de los programadores.

A continuación, intentemos comprender cómo probar la aplicación básica en Angular y tocar una pequeña teoría.

Comencemos con los tipos de pruebas que los desarrolladores de aplicaciones pueden hacer (en nuestro caso, frontend).



(Martin Fowler y Google Testing Blog)

Angular tiene tales métodos listos para usar.

Echemos un vistazo más de cerca a la pirámide de prueba usando el ejemplo de aplicaciones angulares.

Pruebas unitarias: pruebas aisladas de métodos de clase. Verificamos el funcionamiento del método, pasando varias combinaciones de parámetros a la entrada, y comparamos el resultado con el esperado.

Por ejemplo, tenemos un componente en el que las cadenas hexadecimales se convierten en rgb, esta información se muestra al usuario y también se envía al resto del servidor.

Si comenzamos a buscar pruebas unitarias, podemos escribir muchas pruebas para cada método en la clase de componente.

Si tenemos un método rgbToHex (hexadecimal: cadena) => cadena, entonces para probar un método con dicha firma, debemos hacer lo siguiente: esperar (rgbToHex ('777')). ToBe ('rgb (119, 119, 119)') .

Parece genial, pero tenemos un problema debido a la gran cantidad de funciones que deben cubrirse, y nos estamos volviendo demasiado perezosos para escribir pruebas. Además, incluso al escribir pruebas unitarias para cada método en un componente, no garantizamos la exactitud de su trabajo conjunto.

Por ejemplo, después de haber realizado y probado el componente de salida de la tabla, podemos llamarlo en el componente "calculadora", pero especificamos accidentalmente el enlace incorrecto. Entonces los datos no caerán en las tablas y los componentes que funcionan completamente no funcionarán correctamente.

imagen

Pruebas de integración- Prueba de varios componentes. A partir de esta etapa, comenzamos a probar no solo los métodos de clase, sino también su enlace a html, es decir Haga clic en los elementos dentro del componente. Las pruebas superficiales a menudo se encuentran en la notación angular , que es esencialmente una prueba de integración.

En las pruebas superficiales nos detendremos con más detalle a continuación.

Pruebas E2E (de extremo a extremo): una forma de probar la aplicación por completo para resolver los problemas de las pruebas unitarias.

Con este enfoque, escribimos scripts de prueba para una aplicación totalmente renderizada, es decir Todos los componentes y servicios se ensamblan juntos, y reproducimos las acciones del usuario.
Esto es muy bueno, pero podemos tener el problema de cambiar dinámicamente los stubs (generalmente un servidor json estático en node.js), y para reproducir diferentes situaciones, es posible que necesitemos datos diferentes del servidor emulado.

Por ejemplo: tenemos una página con una lista de usuarios, uno de los componentes de este panel es la paginación. Debería comportarse de manera diferente con diferentes números de usuarios. Pero si los datos en el servidor emulado se configuran a través de json, entonces el número de usuarios siempre será uno, y no podremos verificar todos los casos con un número diferente de páginas.

Como resultado, podemos concluir que si queremos cambiar de manera flexible los stubs y probar no por métodos, sino por unidades lógicas de la interfaz, debemos usar algo entre e2e y pruebas unitarias. Aquí es donde las pruebas superficiales (pruebas de integración) vienen al rescate.

Glosario:

  • Los simulacros son objetos para simular respuestas en varios casos de uso.
  • Los stubs son los stubs más estúpidos sin lógica (puedes leer Martin).
  • Karma es un corredor de prueba integrado en angular (a menudo se recomienda usar broma, pero no se trata de eso hoy).
  • Jasmine es un marco para describir pruebas en forma de especificaciones (ver más abajo).
  • spec - extensión del archivo de prueba (descripción de la especificación en estilo BDD).
  • Es el nombre de los métodos para las pruebas en jazmín.
  • xit es el nombre de los métodos que no se ejecutarán.
  • ajuste: si hay métodos con el mismo nombre en las especificaciones, solo se lanzarán.

La prueba superficial para Angular, así como para otros marcos, es un enfoque de prueba unitaria, cuando se representa un componente de tamaño suficientemente grande para que pueda existir como una unidad separada de interfaz de usuario con su propia funcionalidad.

Por ejemplo, tenemos un componente para convertir hexadecimal -> rgb. Solo podemos representar este componente, generar apéndices para diferentes situaciones, ejecutar posibles casos de uso para este componente desde el punto de vista de los usuarios finales y verificar el funcionamiento del componente.

Intentemos encontrar un ejemplo (repositorio).

Prepare la clase para acceder a los componentes del componente de acuerdo con PageObject y agregue un ayudante a la raíz del proyecto.

Ayudante: ayudará a buscar elementos en los componentes que se han seleccionado para renderizar. De esta manera, podemos hacer la vida más fácil si usamos Material angular: luego, los elementos de selección de tipo crearán una lista con opción en un bloque separado, y la búsqueda de estos elementos puede conducir a repeticiones, y un envoltorio en forma de ayuda puede ayudar.

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 es una popular plantilla de prueba automatizada. Se usa para simplificar el soporte para pruebas escritas. Si cambiamos la interfaz de usuario, no tenemos que volver a escribir las pruebas, solo cambie los selectores de elementos.


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')
  }

}

y las pruebas en sí mismas:


// ,        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('');
  });

});

Todo parece ser simple, pero aquí hay algunas notas interesantes para las pruebas de Angular Shallow:

  • Siempre verifique que no haya solicitudes http sin reservas, esto lo ayudará a comprender exactamente si hay solicitudes innecesarias para nuestra funcionalidad.
  • PageObject , .
  • json , .
  • , .
  • .
  • .
  • , session, local, cookie ( ).
  • fake.js.
  • .




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


All Articles