Mailtrap, Testcafe e i controlli automatizzati

Parole
Marco Bellomo
Immagini
Francesca Uguzzoni
Tempo di lettura
10

Un Proof of Concept di un sistema completamente automatizzato per controllare il funzionamento dei form

P

La legge di Murphy

La legge di Murphy, riassunta con l'aforisma "Se qualcosa può andare storto, lo farà", riflette l'idea che, in situazioni complesse o inaspettate, gli eventi negativi hanno una propensione innata a verificarsi. La legge di Murphy serve come monito a considerare scenari alternativi e ad adottare precauzioni, contribuendo a promuovere la pianificazione e la resilienza di fronte alle incertezze della vita, e oseremmo dire del codice.

Edward A. Murphy Jr. è stato un ingegnere aerospaziale statunitense, nato il 13 gennaio 1918 e morto il 17 luglio 1990. Lo conosciamo tutti principalmente per la formulazione della famosa "Legge di Murphy". Murphy ha lavorato come ingegnere per la United States Air Force e ha fatto parte del team coinvolto nel progetto "MX981", un esperimento riguardante l'accelerazione umana nell'ambito dell'equipaggiamento degli astronauti. È durante questo progetto nel 1949, che Murphy è accreditato per aver espresso il suo principio fondamentale: "Se c'è più di una maniera per fare un lavoro e una di queste porterà a un disastro, allora qualcuno lo farà in quel modo". La sua legge è divenuta un pilastro del pensiero umoristico e della filosofia della sfortuna, applicabile in una vasta gamma di contesti, dal mondo scientifico e tecnologico a situazioni più quotidiane.

Questo può apparire piuttosto pessimistico, ma se ci pensiamo bene, quante volte abbiamo azzeccato al primo colpo il modo di inserire una chiavetta usb? Sappiamo perfettamente come farlo eppure ogni volta lo sbagliamo. Applicando una sorta di logica “fattoriale” ai sistemi complessi, immaginiamo quante possibilità binarie ( e più ) esistono in un sistema di media complessità Ecco perché i controlli da fare, su qualsiasi sistema, al minimo cambio diventano una questione importante e lunga.

I test di invio mail in staging

Il processo di test dell'invio di e-mail in un ambiente di staging può rapidamente trasformarsi in un compito noioso e consumare ingenti quantità di tempo. Tradizionalmente, ogni singolo test richiede l'invio di e-mail a indirizzi specifici, spesso coinvolgendo mailbox di sviluppatori o account dedicati per la verifica. Questa procedura, oltre a essere inefficace, è estremamente time-consuming e può portare a involontari invii a destinatari reali, con potenziali conseguenze indesiderate. 

Anche il semplice scrivere “È solo un test di prova, non importa rispondere” comporta un piccolo stress psicologico, nonostante i ripetuti controlli sul cambio dei dati del destinatario, può sempre esserci la remota possibilità che qualcosa sia andato storto, e che quella mail arrivi davvero alla persona sbagliata. Cosa provocherà tutto questo? Ulteriore stress indesiderato anche sul malcapitato.

Inoltre, la necessità di attendere la consegna effettiva delle e-mail e l'apertura delle caselle di posta per verificarne il contenuto comporta una perdita di tempo significativa, rallentando il ciclo di sviluppo e di test. Affrontare questo processo manualmente è non solo noioso, ma può anche generare frustrazione tra gli sviluppatori, riducendo l'efficienza complessiva del team di sviluppo.

Mailtrap, una soluzione a una prima parte del problema

Una prima soluzione a questo tipo di problematiche è l’utilizzo di uno strumento come Mailtrap, essenziale per gli sviluppatori e i team di sviluppo che desiderano semplificare e migliorare il processo di testing delle e-mail. Una delle principali ragioni per utilizzarlo è la sua capacità di fornire un ambiente sandbox sicuro e isolato per testare le e-mail, senza inviare accidentalmente messaggi a destinatari reali. Questo permette agli sviluppatori di verificare l'aspetto e il contenuto delle e-mail generate dalle loro applicazioni, garantendo che siano formattate correttamente e che contengano le informazioni necessarie. 

Mailtrap offre inoltre funzionalità avanzate come l'analisi dell'intestazione e la visualizzazione dell'HTML, consentendo agli sviluppatori di identificare e risolvere eventuali problemi legati alle e-mail, in modo efficiente. Grazie a queste caratteristiche, Mailtrap si rivela uno strumento indispensabile per garantire che le comunicazioni via e-mail siano affidabili e professionali, prima di essere implementate in un ambiente di produzione.

Il settaggio è molto semplice, basta infatti settare Host, Username e Password nel proprio codice ed ecco che Mailtrap cattura qualsiasi email parta dal sito di staging, qualsiasi sia il destinatario. È possibile vedere la stessa nel proprio backend di Mailtrap.

Questo naturalmente risolve solo una parte della questione, anche se è una parte fondamentale. 

La seconda parte (assolutamente noiosa e ripetitiva) è riempire uno svariato numero di volte lo stesso Form, tanto che alla fine sul classico form di contatti, si arriva a mettere A al nome, B al cognome e C al messaggio. Nel messaggio successivo si metterà A1, B1, C1 e così all’infinito, e oltre. 

Quando poi parliamo di form aderenti alla realtà, dobbiamo pensare che useremo un form protetto da recaptcha, poiché un sito in staging, seppur con settaggi particolari, deve avere le funzionalità quanto più aderenti a un sito in produzione, e quindi, va da sé, che i form vadano protetti.

Una tecnica molto utile per ovviare a questo problema è usare il recaptcha di Google, ormai uno standard indiscutibile, usando le chiavi di test, che di fatto bypassano il controllo.

immagine con testo non sono un robot recatptcha testcafe

Naturalmente, abbinando tutto questo a un bel blocco nginx globale sul sito di staging, continuiamo comunque ad essere protetti da qualsiasi tentativo di forzare l’invio mail da qualsiasi bot.

A questo punto arriviamo al secondo strumento che può notevolmente semplificare tutto il nostro flusso di testing: Testcafe.

Testcafe, uno strumento utile per il testing

TestCafe è un interessante alleato (come altri strumenti similari) per il reparto di sviluppo e testing, che consente una validazione accurata e automatizzata delle applicazioni web. Permette, inoltre, di eseguire test su diverse configurazioni di browser e dispositivi, garantendo un'esperienza uniforme agli utenti finali.Può essere in definitiva utilizzato per testare scenari complessi, interazioni utente, e verificare la corretta gestione dei dati. Automatizza in definitiva la parte noiosa di controllo su operazioni standard. 

In soldoni: 

A un certo comportamento utente o richiesta, il sito deve rispondere questo. Lo fa? Bene! Test correttamente superato. Non lo fa? Test fallito.

Tutto questo torna incredibilmente utile dal momento che il comportamento voluto sul sito può essere molto particolare,e non essere facilmente rintracciabile nella memoria del programmatore, designato all’effettuare una modifica su un sito, magari dopo svariati mesi. 

Alcuni particolari infatti possono andare perduti, in alcuni ambiti, inoltre, le macchine oramai ci stanno battendo, soprattutto quando parliamo di noia: è un aspetto importante da considerare per chi frucchia con l’idea di una intelligenza artificiale forte: Davvero vogliamo creare un’entità capace di annoiarsi tanto e quanto noi, se non di più? 

Andrà a finire così: oltre a bypassare o a fare peggio le cose ripetitive e noiose, anche i computer inizieranno a raccontare le nostre piccole confidenze solo per evitare la terribile noia che, come scriveva Leopardi ne Le operette morali e precisamente nel Dialogo di Torquato Tasso e del suo Genio familiare

 «…A me pare la noia sia della natura dell’aria: la quale riempie tutti gli spazi interposti alle altre cose materiali, e tutti i vani contenuti in ciascuna di loro; e donde un corpo si parte, e altro non gli sottentra, quivi ella succede immediatamente…»

Come sempre aggiungiamo ai nostri articoli un piccolo pezzo di testo un po’ fuori contesto, ma aderente al nostro modo di pensare umano, per mostrare che questo scritto è quasi completamente umano ( quasi completamente, c’è anche qui lo zampino di Chatgpt per qualche parte noiosa di questo scritto).

Tutto questo lungo discorso per arrivare a cosa? Al semplice fatto che possiamo scrivere un test automatizzato per inviare una mail tramite il form del sito in staging e catturarla tramite Mailtrap. 

Avremo così una panoramica completa su ciò che è andato a buon fine.

Mettiamo tutto assieme 

Fino a qui abbiamo messo insieme due utilissimi strumenti, ma ci è rimasta una parte comunque un po’ noiosetta, ovvero controllare che effettivamente questa mail sia arrivata su Mailtrap

E se vi dicessimo che anche questo può essere oggetto di automazione?

Prendiamo il classico form di contatto con: Nome, Cognome, Messaggio.

Dato che usiamo Testcafe, che non si stancherà mai di mettere elementi aderenti e completi sui campi del form, potremo sempre indicare che a inviare il form è 

Nome: Sistema di test di

Cognome: Agenzia 

Messaggio: “Ho inviato questo messaggio di test per provare il funzionamento, non importa rispondere.” 

Per maggiore completezza possiamo aggiungere un timestamp del nostro messaggio.

Cos’è un timestamp? Un timestamp è una sequenza di caratteri o numeri che rappresenta una data e un'ora specifiche, solitamente espressa fino al livello di secondo o frazione di secondo. Questo valore temporale è utilizzato per registrare o identificare l'istante in cui si è verificato un evento. Il timestamp di questo messaggio è giorno-mese-anno-ora-minuto-secondo. 

il messaggio può quindi diventare “Ho inviato questo messaggio di test per provare il funzionamento, non importa rispondere. ( timestamp del messaggio)” 

Il punto chiave di tutto ciò è il timestamp, ovvero l’elemento che ci permetterà di fare il check di questa email fra le altre. Nell'esempio lo troverete associato a un'ulteriore stringa alfanumerica casuale per maggiore sicurezza, ma non è necessaria in teoria. 

Ma come facciamo a leggere le mail arrivate su mailtrap? Semplicemente facendo una chiamata alle loro api, tramite questa funzione.

  function searchForStringInMessages(searchString) {
    return new Promise((resolve, reject) => {


        let foundString = false; // Flag per indicare se la stringa è stata trovata

        const options = {
            method: 'GET',
            url: `https://mailtrap.io/api/accounts/${ACCOUNT_ID}/inboxes/${INBOX_ID}/messages`,
            headers: {
                'Accept': 'application/json',
                'Api-Token': MAILTRAP_API_TOKEN
            }
        };

        request(options, function (error, response, body) {
            if (error) {
                reject(error);
                return;
            }

            if (response.statusCode >= 400) {
                console.error("Response error:" + response.statusCode + " " + response.statusMessage);
                reject("Response error:" + response.statusCode + " " + response.statusMessage);
                return;
            }

            const messages = JSON.parse(body);

            let processedMessages = 0;

            messages.forEach(function (message) {
                // Richiesta API per ottenere il corpo del messaggio in formato testo
                const textOptions = {
                    method: 'GET',
                    url: `https://mailtrap.io${message.html_path}`,
                    headers: {
                        'Api-Token': MAILTRAP_API_TOKEN
                    }
                };

                request(textOptions, function (error, response, textBody) {
                    if (error) {
                        reject(error);
                        return;
                    }

                    // Cerca la stringa nel testo del messaggio
                    if (textBody.includes(searchString)) {
                        foundString = true;
                    }

                    processedMessages++;

                    // Controlla se abbiamo elaborato tutti i messaggi
                    if (processedMessages === messages.length) {
                        // Alla fine, risolvi la promessa con il risultato
                        resolve(foundString);
                    }
                });
            });
        });
    });
}


Giusto per completezza, vi lascio tutto il test scritto su testcafe di prova

const axios = require('axios');
const request = require('request');
const dotenv = require('dotenv');
dotenv.config();
/*dati ineiettati da variabili d'ambiente*/
/*dati di mailtrap*/
const MAILTRAP_API_TOKEN = process.env.MAILTRAP_API_TOKEN;
const ACCOUNT_ID = process.env.ACCOUNT_ID;
const INBOX_ID = process.env.INBOX_ID;
/*dati di autenticazione blocco ngin form */
const USERNAME = process.env.USERNAME;
const PASSWORD = process.env.PASSWORD;

function generateRandomString(length) {
    const characters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
    let result = '';

    for (let i = 0; i < length; i++) {
        const randomIndex = Math.floor(Math.random() * characters.length);
        result += characters.charAt(randomIndex);
    }

    return result;
}

function searchForStringInMessages(searchString) {
    return new Promise((resolve, reject) => {


        let foundString = false; // Flag per indicare se la stringa è stata trovata

        const options = {
            method: 'GET',
            url: `https://mailtrap.io/api/accounts/${ACCOUNT_ID}/inboxes/${INBOX_ID}/messages`,
            headers: {
                'Accept': 'application/json',
                'Api-Token': MAILTRAP_API_TOKEN
            }
        };

        request(options, function (error, response, body) {
            if (error) {
                reject(error);
                return;
            }

            if (response.statusCode >= 400) {
                console.error("Response error:" + response.statusCode + " " + response.statusMessage);
                reject("Response error:" + response.statusCode + " " + response.statusMessage);
                return;
            }

            const messages = JSON.parse(body);

            let processedMessages = 0;

            messages.forEach(function (message) {
                // Richiesta API per ottenere il corpo del messaggio in formato testo
                const textOptions = {
                    method: 'GET',
                    url: `https://mailtrap.io${message.html_path}`,
                    headers: {
                        'Api-Token': MAILTRAP_API_TOKEN
                    }
                };

                request(textOptions, function (error, response, textBody) {
                    if (error) {
                        reject(error);
                        return;
                    }

                    // Cerca la stringa nel testo del messaggio
                    if (textBody.includes(searchString)) {
                        foundString = true;
                    }

                    processedMessages++;

                    // Controlla se abbiamo elaborato tutti i messaggi
                    if (processedMessages === messages.length) {
                        // Alla fine, risolvi la promessa con il risultato
                        resolve(foundString);
                    }
                });
            });
        });
    });
}

import { Selector } from 'testcafe';

fixture('Test2 con verifica email')
    .page('url desiderata')
    .httpAuth({
        username: USERNAME,
        password: PASSWORD
    });


const inviaButton = Selector('input[type="submit"][value="Invia"]');
const body = Selector('body');
const codiceRiferimento = `codice-${generateRandomString(5)}-${Date.now()}`;

test('Verifica invio da form con messaggio di ringraziamento', async t => {
    await t
        .typeText('#nome', 'marco')
        .typeText('#cognome', 'bellomo')
        .typeText('#citta', 'pistoia')
        .typeText('#messaggio', codiceRiferimento)
        .click(inviaButton)
    // Ottieni il testo dal corpo della pagina
    const bodyText = await body.textContent;

    // Stampa a console il testo nel corpo della pagina
    console.log('Testo nel corpo della pagina:', bodyText);

    // Verifica se il testo "Grazie! ." è presente nel corpo
    await t.expect(bodyText).contains('Grazie!');
  
});

test('Verifica di effettivo invio email', async t => {
    await t
        const emailFound = await searchForStringInMessages(codiceRiferimento);
        // Verifica se ha trovato la mail, se si bene, altrimenti test fallisce
    await t.expect(emailFound).ok();
});

Naturalmente ricordatevi di mettere il file .env con questi dati

MAILTRAP_API_TOKEN=xxx ( i token alle api di mailtrap)

ACCOUNT_ID=xxx ( il vostro account mailtrap)

INBOX_ID=xxx ( l’indice numerico della vostra inbox su mailtrap)

USERNAME=xxx (username per il vostro blocco su nginx )

PASSWORD=xxx (password per il vostro blocco su nginx)

Abbiamo fornito una ricetta abbastanza completa, ma non spiegata nei minimi passi, per esigenze di spazio. Se desideri un tutorial completo fino all’ultimo passo, segnalalo pure:lo faremo con molto piacere.

Analizza la tua presenza online.

Scrivici per una consulenza gratuita



Richiedi consulenza

Marco Bellomo

Chairman

A diciott'anni pensavo che sarei diventato uno scrittore di fama mondiale e che avrei dominato le classifiche con il mio oscuro ciclo fantasy. A ventiquattr'anni pensavo che il PHP fosse immortale. Oggi mi piace non dare nulla per scontato, forse perché ...