Testing de Angular Material

Cuando deseamos crear algún tipo de contenido web, ya sea crear una nueva página desde cero, ampliar una ya existente o modificarla, algo que suele preocuparnos bastante es como trabajar con el contenido de la misma de manera sencilla, pero quedando un diseño mantenible, flexible y personalizable, de manera que este se adapte al comportamiento que queremos que vaya a tener y la identidad de dicha página.

Para esto, normalmente se usan bibliotecas de componentes, es decir un conjunto de elementos que ya poseen un aspecto visual definido pero adaptable, usando por ejemplo temas y con una funcionalidad que nos facilite el trabajo.

Entre las múltiples opciones que ofrece el mercado una de las más usadas, al menos para desarrollos en Angular, es Angular Material.

David Pinedo Solano
David Pinedo SolanoDp-bts-ct/experience Design & Mobile en VIEWNEXT

¿Qué es Angular Material?

Hace tiempo, ante la disparidad de criterios respecto a cómo debían verse los componentes en la web, el equipo de google decidió diseño un estándar de criterios para los componentes, que incluía tanto estilo, detalles sobre fuentes etc. Esto es conocido como “Material Design”, más tarde para su framework Angular decidieron plasmar dichos estándares conjunto a una funcionalidad concreta que usaban muchas páginas web creando, ahora si, Angular Material.

Estos componentes permiten una gran personalización mediante unos archivos llamados temas, y su funcionalidad permite cubrir múltiples áreas de negocio reutilizando los componentes a través de toda la página.

¿Y qué ocurre con la documentación respecto a pruebas como por ejemplo Test Unitarios?

Por desgracia a diferencia de otros aspectos que están ampliamente documentados, no hay tanta bibliografía sobre test unitarios, lo que motiva precisamente este artículo, dar algo de luz sobre cómo podemos hacer dichas pruebas de ciertos componentes muy utilizados.

A continuación veremos como realizar estas pruebas en algunos de los componentes más utilizados.

Componente tabla

Dado que una tabla de Angular Material tiene bastante variantes, vamos a analizar según sea nuestro caso:

  • Componente tabla básica, es decir una tabla la cual recibe un array o vector de datos como input y nos muestra la tabla con dichos datos, en este caso los test unitarios serian bastante directos, simplemente ‘mockeamos’ el servicio, introducimos un array de valores falsos y comprobamos luego por ejemplo a través de una clase que esta se muestra con los datos.
  • Componente tabla con paginador, en este caso tenemos dos variantes.
    • Utilizamos el paginador propio de angular material
    • Utilizamos un paginador nuestro personalizado y lo conectamos con la tabla.

El primer caso, es el que vamos a analizar, y luego indicaremos la única diferencia que tendríamos si estamos en el segundo caso.

Cuando usamos el paginador de material, dicho paginador se conecta mediante un dataSource. Para ello primero necesitaríamos tener la propiedad que enlaza con el MatPaginator.

@ViewChild(MatPaginator, { static: true }) paginator!: MatPaginator;

Luego lo enlazamos a los datos con:

this.dataSource.paginator = this.paginator;

Así es como se suelen enlazar los datos y el paginador, pero en nuestros casos de test, tendremos que ‘mockear’ dicha propiedad del datasource, para hacer esto tenemos una función de typescript creada específicamente para modificar objectos y añadirles una propiedad “Object.defineProperty”.

De esta manera suponiendo que en los test tenemos el valor por defecto, es decir que component es la instancia del componente que estamos probando (si no sería tan sencillo como cambiar al nombre que usemos por ejemplo page en lugar de component).

Object.defineProperty(component, ‘dataSource’,

{ value: { paginator: { firstPage: () => {} } }

Con dicha línea hemos modificado el objeto component, y la propiedad dataSource la hemos definido con el valor paginator y los métodos que tendría nuestro paginador en el caso del ejemplo firstPage (el cual hemos pedido que no haga nada), o vacío si no tuviera ningún método.

Ahora en nuestro componente tenemos la propiedad con el paginador y sus métodos pueden comprobarse fácilmente, además al ser el paginador una propiedad del componente (recordemos el viewchild), podemos mediante un espía comprobar que sus métodos son llamados o su comportamiento.

Componente Dialog (Desde el punto de vista de quien hace aparecer el modal)

El componente MatDialog es un servicio, de dicho servicio se utilizan principalmente 3 métodos, estos son open, afterclose y close.

Tanto Open como Close son bastante simples, ya que normalmente MatDialog se añade como public a los componentes por lo que no tenemos que inyectarlo, podemos ponerle un espía directamente con spyOn.

Ejemplo

const spyOpen = spyOn(component.dialog, ‘open’).and.callthrough();expect(spyOpen).tohaveBeenCalled();

De esta forma tendríamos un espía que no intercepta el método, es decir, este se ejecuta de forma normal, y luego esperamos que dicho método sea llamado para confirmar que pasó por esa línea.

Sin embargo, AfterClose es más complicado, en este caso AfterClose es un método que se aplica a la referencia que devuelve open, por lo que para ‘mockearlo’, tendremos que ‘mockear’ el retorno del open y en ese valor que retornamos, devolver dicho método. Así un ejemplo sería:

const spyOpen = spyOn(component.matDialog, ‘open’).and.returnValue({ afterClosed: () => of(true)} as MatDialogRef<Componente que abre el matDialog por ejemplo ejemploDialogComponent>)

Componente MatDialogRef (Punto de vista desde el modal que es llamado es decir el modal receptor)

Desde el punto de vista del modal receptor, nos interesaría ‘mockear’ los datos que recibe el modal, aunque hay casos que el modal no recibe ningún dato, por ejemplo una ventana emergente con un botón que según donde pulses te redirige a una dirección u otra.

Lo más habitual es que nuestra ventana emergente recibe algún tipo de dato, para esto tendremos que ‘mockear’ por ejemplo los datos, para ello creamos una variable falsa por ejemplo mockData:

const mockData = { conjunto de datos de entrada falsos }.

Así mismo, si estamos desde la ventana modal probablemente tengamos que mockear el cierre del modal, y podríamos pensar bueno dijimos que era bastante sencillo el método de close y es cierto, pero normalmente en el modal y no en el componente que lo llama, lo que tenemos es una referencia no el servicio matDialog, por lo tanto, tenemos que mockear dicha referencia para cerrar la ventana emergente:

const mockDialogRef = { close(param1: any) {} }; Es decir, tenemos una propiedad mock que tiene el método close que no hace nada.

Ahora solo tenemos que añadir ambos ‘mockeos’ a los providers del componente, para que cuando se fueran a llamar a los auténticos, sean sustituidos por estos que hemos puesto.

Providers: [{provide: MAT_DIALOG_DATA, useValue: mockData},

{provide: MatDialogRef, useValue: mockDialogRef}]

Componentes de ReactiveForms, matinput, matselect

Los componentes matInput y MatSelect, no tienen en si ningún misterio, sin embargo, normalmente cuando tenemos un conjunto de formControls se agrupan en un formGroup y este sí que tiene más miga.

Normalmente declaramos un formGroup usando FormBuilder el cual es un servicio.

Providers: [FormBuilder]

Este tenemos que inyectarlo en una variable como ya vimos antes, y luego creamos el groupControl como se hace normalmente en cualquier componente.

Ejemplo para mockFormGroup.

mockformGroup: FormGroup = formBuilder.group({ controlador1: new FormControl(‘’)});

Esto podemos hacerlo antes del fixture.detectChanges para tenerlo disponible para cada test, o dentro de cada test para poder crear uno con valores diferentes en cada test, incluso podemos añadir o eliminar controles fácilmente con mockFormGroup.addControl(‘nombre del control’, new FormControl(‘valor inicial’, {opciones})), mockFormGroup.removeControl(‘nombre del control’).

Una vez creado el formgroup, el resto es bastante intuitivo, ya que estos falsos formControls poseen la misma funcionalidad que uno de verdad, por lo que podemos comprobar fácilmente los subscribe a valueChanges, para cuando cambia de valor setValue cuando se asigna el valor etc. Usando los spyOn que vimos anteriormente en este post.

 

Otros artículos relacionados

2024-03-06T09:01:35+01:006 marzo, 2024|

¡Compártelo en tus redes sociales!

Ir a Arriba