Il debugger in sessione

Lo strumento quotidiano è il debugger integrato nella AL Language extension di Visual Studio Code, guidato da launch.json. La distinzione che l’esame ama è fra una configurazione di tipo launch, che pubblica e apre una nuova sessione del client partendo eventualmente da un oggetto di startup, e una di tipo attach, che si aggancia a una sessione già esistente sull’ambiente. La prima serve mentre sviluppi, la seconda quando devi cogliere sul fatto un comportamento che si verifica in una sessione utente reale.

Da lì valgono le abitudini di qualunque debugger: breakpoint, breakpoint condizionali, step into/over/out, ispezione delle variabili e del call stack, watch. Due opzioni sono specifiche di Business Central e ricorrono nelle domande: la possibilità di interrompere automaticamente sull’errore, utile quando non sai dove mettere il breakpoint perché non sai da quale evento parta la catena, e la possibilità di entrare nel codice che sta fuori dalla tua estensione — base application o altre app — invece di restare confinato nella tua. Sul modello a eventi di Business Central quest’ultima è spesso l’unica strada per capire in quale punto della base viene sollevato il tuo event publisher.

Snapshot debugging

Il debugger attaccato ferma l’esecuzione: in un ambiente di produzione di un cliente è inaccettabile. Lo snapshot debugging risolve esattamente questo. Configuri una snapshot configuration in launch.json, definisci in anticipo gli snappoint (l’equivalente dei breakpoint, ma non bloccanti), avvii la registrazione, fai riprodurre lo scenario, chiudi la registrazione e scarichi un file di snapshot che poi riesamini offline in Visual Studio Code, ripercorrendo l’esecuzione registrata con i valori delle variabili così com’erano.

I limiti sono il cuore delle domande: gli snappoint vanno decisi prima, perché durante il replay non registri nulla di nuovo; non puoi modificare valori né alterare il flusso; serve la corrispondenza fra lo snapshot e il codice sorgente della versione pubblicata; e servono permessi dedicati sull’ambiente. In compenso funziona dove il debugger interattivo non arriva: sessioni non interattive, job queue, chiamate a web service, errori intermittenti che il cliente non sa riprodurre a comando.

AL Test Framework

I test si scrivono in AL, in una codeunit con Subtype = Test, tipicamente in un’estensione separata che dichiara nel proprio app.json la dipendenza dall’estensione sotto test e dalle test library Microsoft. Le procedure marcate [Test] sono i casi; il trigger OnRun della codeunit viene eseguito prima di ciascuna, ed è il posto giusto per l’inizializzazione. Per verificare che qualcosa fallisca si usa asserterror, non un blocco try generico.

Il tema degli handler è ricorrente: in esecuzione di test non c’è un utente che clicca, quindi ogni dialog, message, confirm, page o request page che il codice apre deve essere gestito da una procedura handler dichiarata nell’attributo [HandlerFunctions(...)]. Se manca, il test fallisce. Le page si pilotano con il data type TestPage, che simula l’interazione senza UI reale.

Infine la prevedibilità dei dati: per impostazione predefinita ogni test viene eseguito con rollback automatico della transazione, così la base dati torna allo stato precedente. Un test che dipende da record lasciati da un altro test, o da demo data presenti solo nel tuo sandbox, è un test fragile: i dati vanno creati nel test stesso.

Telemetria in Application Insights

Debugger e snapshot rispondono a “perché è successo in quella esecuzione”. La telemetria risponde a “cosa succede davvero, quanto spesso, su quali tenant”. Si collega una risorsa Application Insights all’ambiente (lato cliente, dall’admin center) e/o si dichiara la connection string nell’app.json dell’estensione, così che il publisher riceva i segnali della propria app. Business Central emette di suo segnali su errori, permessi mancanti, lock e query SQL lente, apertura di page, report, chiamate web service, job queue, ciclo di vita delle estensioni. Dal codice AL si aggiungono eventi custom con Session.LogMessage, con un event ID stabile, una verbosity e custom dimension utili al filtro. L’analisi si fa in query KQL. È il modo per accorgersi di un problema prima che il cliente lo segnali, e per decidere se vale la pena aprire uno snapshot.

Trappole tipiche d’esame

  • Errore non riproducibile nella produzione SaaS di un cliente, senza bloccare gli utenti → snapshot debugging: l’attach del debugger sospende una sessione reale; lo snapshot registra e si riesamina dopo, offline.
  • Scoprire quanto spesso e su quali tenant accade un errore → telemetria in Application Insights: lo snapshot fotografa una singola esecuzione, non dà aggregati; per trend e frequenze serve la query KQL sui segnali.
  • Il test fallisce appena il codice apre una Confirm o una Message → handler mancante: serve la procedura handler dichiarata in [HandlerFunctions(...)], non un rifacimento del codice per togliere il dialog.
  • Verificare che una procedura sollevi un errore → asserterror: è l’operatore previsto dal framework; una TryFunction cambia la semantica del codice sotto test invece di verificarla.
  • Test che passa da solo e fallisce nella suite → dipendenza dai dati: ogni test deve costruirsi i propri record e contare sul rollback automatico, non ereditare lo stato dal test precedente.
  • Non so dove mettere il breakpoint perché non conosco il punto d’ingresso → break on error: l’interruzione automatica sull’errore, unita allo step nel codice fuori dalla propria estensione, porta al publisher che ha scatenato la catena.