Skip to content

Debugging e Unit Testing

I programmatori inesperti tendono a fare debug riempiendo il codice sorgente di funzioni printf() per capire i valori delle variabili. Un approccio nettamente superiore è utilizzare un Debugger integrato nell’IDE (come Visual Studio o VS Code) o a riga di comando (come il leggendario GNU Debugger, GDB).

Un debugger permette di arrestare il tempo mentre il programma è in esecuzione e ispezionare la memoria, senza modificare il codice per inserire continue stampe su schermo! I concetti chiave sono:

  • Breakpoint (Punto di interruzione): Un segno rosso (o comando GDB come break main.c:42) messo su una riga di codice. Quando l’esecuzione raggiunge quella riga, il programma si congela in pausa.
  • Ispezione Variabili (Locals / Autos / Watch): Mentre il programma è in pausa, il debugger ti mostra in una tabella (o tramite il comando GDB print my_var) tutti i nomi delle variabili locali e il loro esatto valore in quel nanosecondo.
  • Single Step (Passo a passo): Ti permette di eseguire un’istruzione alla volta in GDB o nel tuo IDE.
    • Step Over (GDB: next o n): Esegue la riga corrente e passa alla successiva. Se la riga contiene una funzione, la esegue istantaneamente tutta in un colpo solo.
    • Step Into (GDB: step o s): Se la riga corrente è la chiamata a una funzione, “entra” dentro la funzione per farti analizzare la sua logica interna passo-passo.
    • Step Out (GDB: finish): Esegue il resto della funzione in cui ti trovi e si ferma subito dopo che questa ha fatto return al chiamante.

Una volta che hai debuggato una funzione con successo, non fermarti lì. Scrivi uno Unit Test!

Lo Unit Testing è un processo in cui si scrivono dei mini-programmi (spesso chiamati test suites) il cui unico scopo è testare automaticamente le singole funzioni della tua libreria passandogli input noti e verificando che l’output coincida con le aspettative.

I framework per il C e C++ più famosi sono Google Test (gtest), CUnit e Unity.

Anche se Google Test è scritto in C++, è lo standard industriale per testare librerie scritte in C. Un test si definisce tramite macro come TEST ed EXPECT_EQ.

file_di_test.cpp
#include "gtest/gtest.h"
extern "C" {
#include "mia_libreria_matematica.h" // Importiamo la funzione C da testare
}
// Definiamo un blocco di test
TEST(MatematicaSuite, TestSommaPositiva) {
// Mi ASPETTO (EXPECT) che la funzione somma(2, 2) sia UGUALE (EQ) a 4.
EXPECT_EQ(somma(2, 2), 4);
// Mi aspetto che la funzione restituisca esattamente 100
EXPECT_EQ(somma(99, 1), 100);
}
int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS(); // Avvia l'esecuzione di tutti i TEST() definiti
}

Esercitati a scrivere una logica corretta. Se fosse un unit test, chiameremmo questa funzione più volte verificando l’output! Modifica la funzione per ritornare il numero maggiore tra i due passati come argomento.