La mise en place de tests unitaires pour des algorithmes complexes comme le clustering spectral demande une isolation parfaite des composants, et ce script illustre une maîtrise avancée de la PROC IML pour y parvenir. L'enjeu ici est de valider la "spectralisation" — cette étape cruciale qui transforme des données brutes en un espace de vecteurs propres — en s'assurant que chaque sous-routine (voisinage Gaussien, KNN, normalisation de Laplace) reçoit et traite les bonnes informations.
L'expertise technique réside dans l'utilisation du stubbing (ou mocking) directement au sein d'IML. En redéfinissant localement les fonctions spccGaussNeigh ou spccEigenLRW, vous interceptez les appels système pour vérifier la validité des paramètres (sigma, k, nvecs) avant de retourner une valeur contrôlée. Cette méthode permet de tester la logique de la macro spccSpectralize de manière déterministe, sans dépendre de la complexité numérique des calculs de vecteurs propres réels.
L'astuce d'expert consiste à utiliser call symput pour faire remonter l'état des tests (gauss_correct, l_correct) vers l'environnement macro global. Cela permet une intégration fluide dans un framework de test de type CI/CD, où la variable return_code devient le seul indicateur nécessaire pour stopper ou poursuivre une chaîne de production. Pour des tests encore plus rigoureux, n'hésitez pas à introduire des matrices de test avec des valeurs aberrantes ou des données manquantes pour valider la robustesse de vos routines d'erreurs.
Type : CREATION_INTERNE
Les données sont créées directement dans le code via une matrice IML (`m = {0 1, 0 2, 0 3, 0 4};`). Aucune source de données externe n'est utilisée.
| 1 | %macro spccSpectralize1; |
| 2 | %let gauss_correct = 0; |
| 3 | %let knn_correct = 0; |
| 4 | %let l_correct = 0; |
| 5 | PROC IML; |
| 6 | |
| 7 | package load spectralclust; |
| 8 | start spccGaussNeigh(m, sigma); |
| 9 | expected = {0 1, 0 2, 0 3, 0 4}; |
| 10 | IF sigma = 0.5 & m = expected THEN DO; |
| 11 | call symput('gauss_correct', '1'); |
| 12 | END; |
| 13 | ret = {0 3 2 1, 3 0 3 2, 2 3 0 3, 1 2 3 0}; |
| 14 | return ret; |
| 15 | finish; |
| 16 | |
| 17 | start spccKNNNeigh(m, k); |
| 18 | expected = {0 3 2 1, 3 0 3 2, 2 3 0 3, 1 2 3 0}; |
| 19 | IF k = 2 & m = expected THEN DO; |
| 20 | call symput('knn_correct', '1'); |
| 21 | END; |
| 22 | ret = {0 3 2 0, 3 0 3 2, 2 3 0 3, 0 2 3 0}; |
| 23 | return ret; |
| 24 | finish; |
| 25 | |
| 26 | start spccEigenLRW(m, nvecs); |
| 27 | expected = {0 3 2 0, 3 0 3 2, 2 3 0 3, 0 2 3 0}; |
| 28 | IF nvecs = 2 & m = expected THEN DO; |
| 29 | call symput('l_correct', '1'); |
| 30 | END; |
| 31 | ret = {1 1 1, -2.1638 0.2889 1, 2.1639 -0.2888 1, -1 -1 1}; |
| 32 | return ret; |
| 33 | finish; |
| 34 | |
| 35 | m = {0 1, 0 2, 0 3, 0 4}; |
| 36 | expected = {1 1 1, -2.1638 0.2889 1, 2.1639 -0.2888 1, -1 -1 1}; |
| 37 | out = spccSpectralize(m, 2, 'normalizedRW', 'gaussian', 0.5, 'knn', 2); |
| 38 | correct = all(out = expected); |
| 39 | IF &l_correct & &gauss_correct & &knn_correct & correct THEN call symput('return_code', '0'); |
| 40 | ELSE call symput('return_code', '1'); |
| 41 | QUIT; |
| 42 | %mend; |
| 43 | |
| 44 | %test(spccSpectralize1); |
| 1 | %macro spccSpectralize2; |
| 2 | %let gauss_correct = 0; |
| 3 | %let knn_correct = 0; |
| 4 | %let l_correct = 0; |
| 5 | PROC IML; |
| 6 | |
| 7 | package load spectralclust; |
| 8 | start spccGaussNeigh(m, sigma); |
| 9 | expected = {0 1, 0 2, 0 3, 0 4}; |
| 10 | IF sigma = 0.5 & m = expected THEN DO; |
| 11 | call symput('gauss_correct', '1'); |
| 12 | END; |
| 13 | ret = {0 3 2 1, 3 0 3 2, 2 3 0 3, 1 2 3 0}; |
| 14 | return ret; |
| 15 | finish; |
| 16 | |
| 17 | start spccMutKNNNeigh(m, k); |
| 18 | expected = {0 3 2 1, 3 0 3 2, 2 3 0 3, 1 2 3 0}; |
| 19 | IF k = 2 & m = expected THEN DO; |
| 20 | call symput('knn_correct', '1'); |
| 21 | END; |
| 22 | ret = {0 3 2 0, 3 0 3 2, 2 3 0 3, 0 2 3 0}; |
| 23 | return ret; |
| 24 | finish; |
| 25 | |
| 26 | start spccEigenLSym(m, nvecs); |
| 27 | expected = {0 3 2 0, 3 0 3 2, 2 3 0 3, 0 2 3 0}; |
| 28 | IF nvecs = 2 & m = expected THEN DO; |
| 29 | call symput('l_correct', '1'); |
| 30 | END; |
| 31 | ret = {1 1 1, -2.1638 0.2889 1, 2.1639 -0.2888 1, -1 -1 1}; |
| 32 | return ret; |
| 33 | finish; |
| 34 | |
| 35 | m = {0 1, 0 2, 0 3, 0 4}; |
| 36 | expected = {1 1 1, -2.1638 0.2889 1, 2.1639 -0.2888 1, -1 -1 1}; |
| 37 | out = spccSpectralize(m, 2, 'normalizedSym', 'gaussian', 0.5, 'mutual_knn', 2); |
| 38 | correct = all(out = expected); |
| 39 | IF &l_correct & &gauss_correct & &knn_correct & correct THEN call symput('return_code', '0'); |
| 40 | ELSE call symput('return_code', '1'); |
| 41 | QUIT; |
| 42 | %mend; |
| 43 | |
| 44 | %test(spccSpectralize2); |