2012-02-13 4 views
6

È piuttosto semplice (se noioso) eseguire il test unitario dei moduli di estensione Python scritti in C, inclusi i casi di errore per molte API Python/C come PyArg_ParseTuple. Ad esempio, il modo idiomatica ad iniziare una funzione C che implementa una funzione Python o un metodo simile a:In che modo un'unità prova la gestione delle condizioni di errore per le API Python/C come PyType_Ready e PyObject_New?

if (!PyArg_ParseTuple(args, "someformat:function_name")) { 
     return NULL; 
    } 

Il caso successo di questo può essere unità testata chiamando la funzione con il numero e il tipo corretto di argomenti. I casi di errore possono anche essere verificati chiamando la funzione con il numero di argomenti errato e il numero corretto di argomenti, ma passando i valori del tipo sbagliato. Ciò si traduce in una copertura completa del test del codice C.

Tuttavia, non è chiaro come esercitare i percorsi negativi per altre API Python/C. Un modo idiomatico per iniziare l'inizializzazione del modulo in un'estensione C si presenta come:

if (PyType_Ready(&Some_Extension_Structure) < 0) { 
     return 0; 
    } 

Come può PyType_Ready essere fatto per fallire? Analogamente, la funzione C per allocare una nuova istanza di un tipo di estensione utilizza spesso un API come PyObject_New:

self = PyObject_New(Some_Structure, &Some_Extension_Structure); 
    if (self == NULL) { 
     return NULL; 
    } 

Come si può unit test questo caso negativo (soprattutto considerando PyObject_New è probabile utilizzare molte, molte volte nel corso l'esecuzione di un metodo di test unitario)?

Sembra possibile creare una soluzione generale, basandosi su trucchi linker dinamici come LD_PRELOAD per fornire falsi di queste API C che possono essere indirizzate a fallire nel modo giusto al momento giusto. Il costo di costruire un sistema come quello sembra un po 'fuori portata, però. Qualcun altro l'ha già fatto e rende disponibile il risultato?

Esistono trucchi specifici per Python/C che potrebbero facilitare questo test?

Devo pensare interamente ad altre linee?

risposta

2

Questo è un caso chiaro per i doppi di prova (ad esempio, simulazione). Poiché l'API Python C non offre alcuna possibilità di falsificare una condizione di memoria esaurita, è necessario farlo da soli.

Creare il proprio livello che fornisce PyType_Ready e PyObject_New. Invitali a passare alle funzioni API C, a meno che qualche controllo, probabilmente una variabile d'ambiente, non li istruisca. Possono causare qualsiasi confusione che desideri e testare la reazione del tuo codice.

+1

Grazie Ned. Il problema con i test double è che C non è molto adatto al "monkey patching" che è così semplice in Python e l'API Python/C sembra non fornire alcun modo per iniettare implementazioni alternative. Se ho intenzione di seguire questa strada, ho bisogno di uno speciale corridore di test multi-processo che possa abusare del linker in modo appropriato; Ho bisogno di un modo per determinare il contesto di chiamata in ogni funzione falsa (quindi uso solo il mio codice, non il resto del runtime di Python), ecc. Posso vederlo, ma la strada sembra lunga e difficile ... Io davvero voglio qualche soluzione intelligente che sia più a portata di mano. :/ –

+0

Sì, C è fragile come quello. Intendevo che avresti scritto una funzione che si chiamava PyType_Ready o che restituiva un errore, in base alla variabile di ambiente. Dovresti mischiare quel codice "test double" nel tuo codice reale e lasciarlo sempre lì. Non elegante come un vero test double, ma ti consente di testare il tuo codice. –

Problemi correlati