DevOps serie CI/CD en SAP BTP (Parte 6)

Esta es una serie de entradas para configurar y deployar de manera eficiente mediante SAP BTP nuestras aplicaciones.

Hoy mención especial al testing des de BAS y automatizado, por desgracia, en el momento que he escrito este post, el servicio de testing mediante chromium no funciona bien del todo. Aun así os dejo todas las instrucciones por si se arregla.

Validaciones automáticas de código

Test unitarios automáticos

Preparando QUnit

El siguiente paso es preparar los test unitarios. Estos se deben desarrollar de manera manual, pero nos permiten añadir checks para acelerar y automatizar algunos test. Podemos testear por ejemplo formatters, o funciones para asegurar que los futuros cambios no contienen bugs que puedan afectar al programa.

Serán necesario algunos preparativos previos para nuestra aplicación de test. Para ilustrar este paso solo validaremos que el controlador arranca con el onInit y que los textos están bien traducidos.

Crearemos nuevos ficheros i18n con traducciones en español e inglés. Estos son los añadidos en nuestra aplicación:

En cada archivo añadimos los textos traducidos, para este ejemplo solo utilizaré el texto «text».

Lo siguiente sera programar los test del controlador de la primera vista, por lo que añadiremos el siguiente código en el js:

Y este es el código que añadiremos, mucha atención a los IDs del controlador y de la aplicación.

/*global QUnit*/

sap.ui.define([
	"sap/ui/base/ManagedObject",
    "sap/ui/core/mvc/Controller",
    "ecastella/cdci_btp/controller/View1.controller",
	"sap/ui/thirdparty/sinon",
	"sap/ui/thirdparty/sinon-qunit",
	"sap/ui/model/resource/ResourceModel"
], function (ManagedObject,Controller,AppController,sinon,sinonqunit,ResourceModel) {
	"use strict";
    
	QUnit.module("Controller View1 check",
	{
			before: function () {
                //Carga de los ficheros de texto
				this.applicationName = "ecastella.cdci_btp";
				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")
				});
			},
		
			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() {
            // Eliminamos las vistas una vez finalizada la UT
			Controller.prototype.getView.restore();
            this.oViewStub.destroy();

		},
		
			teardown: function () {
                // Por ultimo eliminamos el controlador
				this._oResourceModel.destroy();
			}
		}
    );

    QUnit.test("Check init controller", function (assert) {
		//Obtenemos el controlador
		//Lanzamos el metodo on init
		this.oAppController.onInit();
		assert.ok(this.oAppController);
	});
    
    QUnit.test("Check text i18n", function (assert) {
		//Obtenemos el controlador
		var texti18nEs = this._oResourceModel_es.getResourceBundle().getText("text");
		var texti18nEn = this._oResourceModel_en.getResourceBundle().getText("text");
		assert.strictEqual(texti18nEs,"Esto es un ejemplo", "Text (ES) correct");
		assert.strictEqual(texti18nEn,"This is an example", "Text (EN) correct");
	});

});

Con todo esto, tenemos dos visiones distintas, la del desarrollador que ejecutará los test a medida que vaya finalizando sus módulos y la del transport management que ejecutará los test y bloqueará la subida en caso de error.

Lo siguiente es generar el marco de pruebas, que como en el caso anterior, aunque ya lo tenemos incluido en la plantilla del proyecto, para CI/CD necesitaremos añadirlo en la raíz de la aplicación (carpeta webapp)

Añadimos al fichero package.json dentro de la carpeta webapp las dependencias de Karma que nos permitirá simular el navegador para ejecutar los test QUnit, que nos permitirá ejecutar los unit test:

        "karma": "^5.0.4",
        "karma-chrome-launcher": "^3.1.0",
        "karma-coverage": "^2.0.2",
        "karma-ui5": "^2.1.0"

Este es el resultado:

Añadimos los scripts de Karma

        "test": "npm run karma-ci",
        "karma-ci": "karma start karma-ci.conf.js

Ahora en la misma carpeta webapp añadiremos el fichero «karma.conf.js», este fichero tiene la configuración de navegador que se utilizará, este será el contenido:

module.exports = function(config) {
    "use strict";
    config.set({
        frameworks: ["ui5"],
        browsers: ["Chrome"],
        browserConsoleLogOptions: {
            level: "error"
        }
    });
};

Añadimos el fichero «karma-ci.conf.js» con el resto de configuraciones, entre ellas las carpetas donde se alojan los ficheros a simular de la aplicación. El objetivo es ejecutar los test de QUnit, así que estos serán los ficheros incluidos en la configuración.

module.exports = function(config) {
    "use strict";
    require("./karma.conf")(config);
    config.set({
        preprocessors: {
            "{webapp,webapp/!(test)}/!(mock*).js": ["coverage"]
        },
        coverageReporter: {
            includeAllSources: true,
            reporters: [
                {
                    type: "html",
                    dir: "coverage"
                },
                {
                    type: "text"
                }
            ],
            check: {
		statements: 100,
		branches: 100,
		functions: 100,
		lines: 100
                }
            }
        },
        reporters: ["progress", "coverage"],
        browsers: ["ChromeHeadless"],
        singleRun: true
    });
};

Ya esta toda la configuración preparada para poder ejecutar por parte de CD/CI nuestra aplicación demanera automática.

UTs iniciadas por el desarrollador (QUnit)

En tiempo de desarrollo se pueden ir lanzando de manera automática las pruebas unitarias a fin de validar la aplicación de una manera rápida y poder conocer de antemano so podremos hacer el deploy hacia productivo.

Para poder ejecutar los test de manera rápida des de Business application studio nos fijaremos en los comandos que el template de la aplicación nos da, para ello iremos al fichero package.json:

Para ejecutar el test podemos pasar el ratón por sobre del script que queremos ejecutar (esperando un par de segundos) y pulsamos «Run script».

La otra manera es por consola, que es como lo ejecutará nuestro sistema de CI/CD. Para lanzar los test vamos a la consola y ejecutamos el comando «npm run unit-tests»:

Y este será el resultado, automáticamente se abre una nueva pestaña en el navegador y si todo esta correcto veremos los test en verde, los errores los dejamos para cuando tengamos integrados los test a la generación del MTA.

Acordaros de matar el proceso «control + c» para que no de muchos problemas durante el desarrollo. Esto nos permitirá agilizar durante el desarrollo las pruebas unitarias

Validación automática de código

Preparando ESlint

Una cosa que puede ser interesante, sobre todo en Javascript, donde el código no va compilado, es pasar validaciones de código para poder comprobar de manera automática si tenemos alguna cosa en el código mal.

Un ejemplo seria no cerrar una instrucción de código en Js con «;» . Esto no seria un error ya que nuestro navegador puede interpretarlo, pero no esta de mas que no tengamos estos fallos en el código.

Aunque este paso lo podemos hacer automático con el template, el template lo prepara para tener todo en la raíz por lo que luego SAP Continuous Integration and Delivery no lo reconocería.

Por lo que vamos a hacer las siguientes configuraciones en nuestro proyecto des de BAS:

Creamos un fichero llamado «.eslintrc» un la raíz de la carpeta webapp con el siguiente contenido:

{
  "plugins": ["@sap/ui5-jsdocs","eslint-plugin-fiori-custom"],
  "extends": ["plugin:@sap/ui5-jsdocs/recommended", "eslint:recommended","./node_modules/eslint-plugin-fiori-custom/configure.eslintrc"],
  "ignorePatterns": ["webapp/localservice/**", "webapp/test/**","test/**"]
}

El código anterior nos permite añadir los pluguins de eslint, los extends o normas a ejecutar (yo he puesto las que recomienda SAP) y por último carpetas que no aplican, como por ejemplo la de test o la de servidor local.

Lo siguiente es crear un fichero «package.json» para poder tener librerías node_modules dentro del proyecto html5, es decir en la carpeta webapp igual que en el paso anterior.

Y este sera su contenido, donde tenemos las librerías necesarias y el script para ejecutar la validación:

{
    "name": "cdci_btp",
    "version": "0.0.1",
    "private": true,
    "description": "A Fiori application.",
    "keywords": [
        "ui5",
        "openui5",
        "sapui5"
    ],
    "main": "webapp/index.html",
    "scripts": {
        "lint": "eslint ./ --rulesdir ./node_modules/eslint-plugin-fiori-custom/lib/rules/"
    },
    "devDependencies": {
        "eslint": "7.32.0",
        "@sap/eslint-plugin-ui5-jsdocs": "2.0.5",
        "@sapui5/ts-types": "1.92.2",
        "eslint-plugin-fiori-custom": "2.2.1",
        "@babel/eslint-parser": "7.14.7",
        "@sap/ui5-builder-webide-extension": "1.0.x",
        "ui5-task-zipper": "^0.3.1",
        "mbt": "^1.0.15"
    },
    "ui5": {
        "dependencies": [
            "@sap/ux-ui5-tooling",
            "@sap/ui5-builder-webide-extension",
            "ui5-task-zipper",
            "mbt"
        ]
    }
}

Este es el resultado:

Por último instalaremos las librerias con el comando «npm install» des del terminal del BAS y en la carpeta «webapp»

Validando el código con eslint por parte del desarrollador

Podemos validar el código cuando queramos, solo tendremos que ejecutar el comando «npm run lint» y esperar a los resultados:

  • Encaso de warnings o todo correcto veremos algo así
  • En caso de errores veremos algo así

Como podéis ver, gracias a estos controles, podemos validar pequeños fallos que pueda tener el código y depurarlos antes de cualquier despliegue del código.

UTs iniciadas de manera automática por CI/CD

*En el momento de escribir esta entrada, el servicio de Karma no esta 100% operativo, aunque en la documentación oficial consta que si que se puede usar Karma. Así que os añado los pasos de como quedaría una vez montado

Preparando Karma (lanzamiento de test en fondo)

Lo siguiente sera configurar el sistema de CI/CD para que ejecute los mismos test que ejecuta el desarrollador pero de una manera automática para que no se puedan descartar estos test.

Nuestro objetivo será automatizar el JOB ya que si utilizamos directamente qUnit, CD/CI nos ejecutará el servidor de test pero quedará en una espera eterna (bueno…. nos fallará al los 10 minutos ).

Así que vamos añadir Karma a nuestro proyecto para poder lanzar en background los test. lo primero sera ejecutar los comandos para añadir las librerías de Karma a nuestro proyecto (fichero package.json). Lo siguientes comandos los ejecutaremos en BAS mediante un terminal y situados en la carpeta del proyecto

npm install --save-dev karma
npm install --save-dev karma-ui5
npm install --save-dev karma-chrome-launcher
npm install --save-dev karma-coverage

Tambien añadiremos un navegador que se ejecuta en fondo, en este caso chrome:

npm install chromium

Este sera el resultado que tendremos en nuestro fichero package.json:

Una vez todo instalado, añadiremos un nuevo fichero llamado «karma.conf.js» en la raíz del de nuestro proyecto y añadiremos este código que contiene la configuración mínima para poder arrancar nuestro simulador de navegador.

module.exports = function(config) {
	"use strict";

	var chromeFlags = [
		"--window-size=1280,1024"
	];

	config.set({

		frameworks: ["ui5"],

		browsers: ["ChromeHeadless"],

		browserConsoleLogOptions: {
			level: "error"
		},

		customLaunchers: {
			CustomChrome: {
				base: "Chrome",
				flags: chromeFlags
			},
			CustomChromeHeadless: {
				base: "ChromeHeadless",
				flags: chromeFlags
			}
		},

	});
};

Ahora indicaremos los ficheros que tenemos que ejecutar con el navegador en fondo. Crearemos un fichero llamado «karma-ci.conf.js» donde indicaremos que ejecutar. Como el fichero anterior, lo añadimos en la raíz y este será su contenido:

module.exports = function(config) {
	"use strict";

	require("./karma.conf")(config);
	config.set({

		preprocessors: {
			"{webapp,webapp/!(test)}/*.js": ["coverage"]
		},

		coverageReporter: {
			includeAllSources: true,
			reporters: [
				{
					type: "html",
					dir: "coverage"
				},
				{
					type: "text"
				}
			],
			check: {
				each: {
					statements: 100,
					branches: 100,
					functions: 100,
					lines: 100
				}
			}
		},

		reporters: ["progress", "coverage"],

		browsers: ["CustomChromeHeadless"],

		singleRun: true

	});
};

Así nos quedará la carpeta con los ficheros de configuración.

Nos queda modificar el fichero packaje.json para añadir las instrucciones para ejecutar Karma dentro del TAG «scripts»:

	"karma-ci": "rimraf coverage && karma start karma-ci.conf.js",
	"unittestkarma": "npm run karma-ci",

Este es el resultado:

Añadiendo los test de Karma – QUnit en CI/CD

*En el momento de escribir esta entrada, el servicio de Karma no esta 100% operativo, aunque en la documentación oficial consta que si que se puede usar Karma. Así que os añado los pasos de como quedaría una vez montado.

Vamos a editar nuestro JOB en SAP Continuous Integration and Delivery:

Y añadimos la configuración para lanzar el test:

Ya lo tenemos todo apunto, dispararemos el job para generar la nueva version del MTAR que contiene el testing y que ejecutará de manera automática.

Ahora vemos como se ejecuta un paso adicional

Y aquí es cuando da error, a diferencia del comando ESLint, que si que ha funcionado sin problema.


Este post corresponde a una serie de post relacionados con DevOps y en concreto con la parte de CI/CD. Si te ha gustado suscribete, dale a la campanita de notificaciones y comparte en redes para estar a la última. Vota Like / Dislike para aportar feedback.

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.