QUnit/OPA/Karma – Vol 1 – Test unitarios de UI5

por | julio 13, 2020

Introducción

Hoy empezaré la casa por el tejado ya que antes de seguir con esta entrada os recomiendo un gran post de Jorge Motos http://programandosap.com/testing-en-sap-ui5/ donde da una gran introducción sobre qué es Qunit.

En esta entrada os enseñaré lo fácil que es integrar test unitarios a vuestro código y cómo ejecutarlos con Karma, una utilidad javascript que nos permitirá lanzar de manera automática este tipo de test.

Esta entrada se engloban con otras ya publicadas y algunas que están por venir para crear un sistema de CI/CD (integración y entrega continua).

Preparando la aplicación

Lo primero será construir una aplicación de ejemplo. Para darle un poco de vidilla esta aplicación serán 2 pantallas

Pantalla 1: un campo input con un formatter

Pantalla 2: Lista con boton de añadir y texto i18n

No buscamos una aplicación perfecta, sino algo de ejemplo 😉

  • La aplicación la crearemos con webide usando el template de SAPUI5 para cloudfoundry. Esta será la estructura resultante, esto nos creará una aplicación sencilla y lo más importante, la carpeta test:
  • Lo siguiente será añadir todo lo necesario para luego jugar con los test, es decir input + formatter + lista + navegación. Para acelerar el proceso, dejo aquí el proyecto «Boton Descarga» (contiene todo lo que vamos a explicar):

Aquí tenéis la aplicación:

Preparando nuestros test con Qunit

Es momento de programar nuestros test unitarios.

La automatización de test no es una cosa que se haga en 5 minutos o de manera mágica, requiere su tiempo. No os aventurais nunca a anunciar un test automático sin valorar el coste de construir ese test.

Vamos a ir por partes, primero crearemos (o en nuestro caso, modificaremos) la carcasa que permite ejecutar todos los test, luego ejecutaremos el test del controlador 1 y luego el del dos.

Estructura de los test

Al crear nuestro proyecto, la plantilla ya tiene de inicio la carpeta Test que contiene los ejemplos básicos de Qunit y OPA.

Pasamos a detallar los ficheros:

  • testsuite.qunit.html: Fichero para la ejecución de los test des del navegador.
  • testsuite.qunit.js: Fichero que contiene la configuración de test a lanzar. Originalmente tiene la ejecución de «AllTest» para Qunit y OPA.
  • unit/controller: Aquí programaremos los test para cada uno de nuestros controladores.
  • unit/AllTests.js: En este fichero añadimos todos los controladores/test que queremos ejecutar de la carpeta controller.
  • unit/unitTests.qunit.html: Otro fichero con la parte visual para el navegador.
  • unit/Test.qunit.html: Configuración de arranque de los test. Aquí se hace referencia al fichero AllTest.js para la ejecución de los test.

En este punto pensaréis, «muy bien, pero… ¿por donde empiezo?». Vamos a empezar con algo sencillo. Vamos a validar que un formatter funciona de manera correcta.

Verificando un formatter – Vista1

Llegados a este punto, podríamos crear un test que valide de manera independiente un formatter, pero nosotros los vamos a lanzar a través del controlador para asegurarnos que el controlador ha añadido de manera correcta el formater.

En el ejemplo que os añado a esta entrada tendréis todo el testing montado

Nos centraremos en el fichero que de manera automática se nos ha creado. este esta en /test/unit/controller/View1.controller.js:

  • Lo primero será añadir las librerias de trabajo, estas son las encargadas de manejar el controlador, importamos tambien el controller (le diremos AppController) y las librerías sinon que nos permiten comparar valores:
sap.ui.define([
	"sap/ui/base/ManagedObject",
	"sap/ui/core/mvc/Controller",
	"qunit/qunit/controller/View1.controller",
	"sap/ui/thirdparty/sinon",
	"sap/ui/thirdparty/sinon-qunit"
], function (ManagedObject,Controller,AppController,sinon,sinonqunit) {
  • Una vez tenemos las librerías, ya podemos programar nuestro test. Aquí lo vamos a programar todo dentro de un solo módulo para no alargar la explicación y para dar rienda suelta a vuestro ingenio. Añadimos al módulo la creación del controlador y la validación de vista.
QUnit.module("View1 Controller",
{
	beforeEach: function() {
		this.oAppController = new AppController();
		this.oViewStub = new ManagedObject({});
		//Obtenemos la vista associada al controlador
		sinon.stub(Controller.prototype, "getView").returns(this.oViewStub);
	},

	afterEach: function() {
		Controller.prototype.getView.restore();
		this.oViewStub.destroy();
	}
}
  • El paso anterior nos inicia en cada test el controlador (que luego destruye). Ahora ya podemos añadir nuestro test:
QUnit.test("Check formatter in controller", function (assert) {
//Validamos el formatter dentro del controlador
		//Obtenemos el controlador
	var oAppController = new AppController();
	assert.strictEqual("", oAppController.formatter.allwaysno("") , "Formatter was correct with blank");
	assert.strictEqual("NO", oAppController.formatter.allwaysno("Any") , "Formatter was correct without blank");	
});

Con estas pocas líneas de código, al ejecutar Qunit validaremos la salida de formater. En este caso el formatter es muy básico, si no le pasas ningún valor retorna «» y si le pasas algún valor retorna «NO». Si alguien modifica la salida de este formatter, Qunit nos avisará del error. (nota: si no sabéis ejecutar los test, un poco más abajo os enseño como)

Ejemplo de ejecución correcta
Modificando el formatter para forzar el error

Verificando traducciones y botones de acción – Vista2

Ya tenemos nuestra vista 1 validada, ahora nos queda la 2. Como son pruebas unitarias, no vamos a probar la navegación o otros aspectos, si que probaremos de manera individual cada controlador ( OPA nos ayudara con esto, pero ya llegaremos ):

En el ejemplo que os añado a esta entrada tendréis todo el testing montado

Ahora deberemos añadir un nuevo fichero y la configuración necesaria en, vamos por partes:

  • Lo primero es añadir un nuevo fichero para los test del controlador de la vista 2, nos podemos copiar el fichero View1.controller.js para empezar y renombrarlo:
  • Lo siguiente es añadir estos nuevos test en AllTest.js para que se ejecuten directamente, el fichero quedará de la siguiente manera:
sap.ui.define([
	"qunit/qunit/test/unit/controller/View1.controller",
	"qunit/qunit/test/unit/controller/View2.controller"
], function () {
	"use strict";
});
  • Ya podemos empezar a programar nuestros test. Como antes, vamos al fichero /test/unit/controller/View2.controller.js y añadimos las librerias:
sap.ui.define([
	"sap/ui/base/ManagedObject",
	"sap/ui/core/mvc/Controller",
	"qunit/qunit/controller/View2.controller",
	"sap/ui/thirdparty/sinon",
	"sap/ui/thirdparty/sinon-qunit",
	"sap/ui/model/resource/ResourceModel"
], function (ManagedObject,Controller,AppController,sinon,sinonqunit,ResourceModel) {
  • A partir de aquí primero prepararemos para cada test unas primeras acciones fijas (solo como experimental) y luego programaremos los test.
  • Lo primero es añadir antes de ejecutar los test la carga del i18n, esto es porque queremos forzar la carga de distintos idiomas para validar traducciones y textos.
before: function () {
     this.applicationName = "qunit.qunit";
     sap.ui.getCore().getConfiguration().setLanguage( "es" );
     this._oResourceModel_es = new ResourceModel({
           bundleUrl : jQuery.sap.getModulePath(this.applicationName, "/i18n/i18n.properties")
     });
     sap.ui.getCore().getConfiguration().setLanguage( "en" );
     this._oResourceModel_en = new ResourceModel({
          bundleUrl : jQuery.sap.getModulePath(this.applicationName, "/i18n/i18n.properties")
     });
},
  • Una vez cargado (esto nos servirá para todos los test) vamos a cargar el controlador en cada test y destruirlo. Esto no sería necesario, pero lo pongo para que veais las distintas opciones (carga al inicio del módulo / carga al inicio de cada test / carga dentro del test )
beforeEach: function() {
	this.oAppController = new AppController();
	this.oViewStub = new ManagedObject({});
	//Obtenemos la vista associada al controlador
	sinon.stub(Controller.prototype, "getView").returns(this.oViewStub);
},

afterEach: function() {
	Controller.prototype.getView.restore();
	this.oViewStub.destroy();
},
  • Por último destruiremos el modelo al final de todo de la ejecución
teardown: function () {
	this._oResourceModel.destroy();
}
  • Ya podemos añadir los test. En este caso añadiremos tres test distintos, el primero validamos la creación del modelo que hacemos en la función onInit(), luego validamos que se añade un nuevo registro en el listado y por último validamos la traducción del texto.
  • Añadimos primeramente el check de traducciones, en este caso usaremos sinon para validar si lo que nos devuelve la carga del i18n es el mismo que el texto que indicamos
QUnit.test("Check text i18n", function (assert) {
	//Obtenemos el controlador
	var texti18nEs = this._oResourceModel_es.getResourceBundle().getText("mytext");
	var texti18nEn = this._oResourceModel_en.getResourceBundle().getText("mytext");
	assert.strictEqual(texti18nEs,"Mi texto", "Text (ES) correct");
	assert.strictEqual(texti18nEn,"My text", "Text (EN) correct");
});

Este es el resultado del test:

  • Lo siguiente es validar que el modelo se carga bien, aquí lo simplificamos en ver si se ejecuta el método onInit de manera correcta
	QUnit.test("Check init controller", function (assert) {
		//Obtenemos el controlador
		//Lanzamos el metodo on init
		this.oAppController.onInit();
		assert.ok(this.oAppController);
	});
  • No pongo ejemplo de la ejecución y añadimos directamente el siguiente test, así lo vemos todo en conjunto. En este caso, iniciamos el controlador con la función onInit y llamamos a la misma función que llama el boton por pantalla, por último cargamos el modelo y validaremos si hay una nueva fila (es decir si length <> 0)
QUnit.test("Check add new row", function (assert) {
	//Obtenemos el controlador
	//Lanzamos el metodo on init
	this.oAppController.onInit();
	this.oAppController.onPress();
	assert.strictEqual(this.oAppController.getView().getModel().getProperty("/data").length.toString(),"1", "Row added correct");
	assert.ok(this.oAppController);
});

Ahora si ejecutamos de nuevo y vemos el resultado:

Lanzar nuestros test de manera manual

En una próxima entrada veremos cómo lanzar de manera automática los test. Para esta entrada, lo haremos de la siguiente manera:

  • Nos situamos en la carpeta ui5 de nuestro proyecto y pulsamos botón derecho:
  • Seleccionamos el boton «+» y «Run as Unit Test».
  • Añadimos el nombre que queramos y como fichero: unitTest.qunit.html

Cuando seleccionamos el «File Name» vereis que podeis ejecutar también OPA, Qunit o la test suite que tiene todo.


Como veis si nos centramos en la programación de los test no deja de ser lo mismo que llamar a funciones del controlador y comparar el resultado. Otra cosa es pensar y programar esos test.

Como siempre suscribete y comparte en redes para ayudar este blog. Como ves, he añadido publicidad en el Blog, ha sido una cosa muy meditada, pero me ayudará a sufragar parte del coste del hosting.

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.