SAS-programmering med sikkerhedsnet: FUTS (Framework for Unit Testing SAS programs) Forkerte analyseresultater fra virksomhedens egen SAS-programmør kan have store omkostninger for virksomheden. Derfor er FUTS et spændende værktøj, som mange der udvikler programmer og applikationer i SAS kan have glæde af. Ved at bruge FUTS-principperne i sine arbejdsrutiner, kan man med et simpelt setup få et overblik over, hvordan ændringer i SASkoden påvirker output-resultaterne. Unit Testing Hvis man følger grundtanken i Unit Testing, skal man lave sine test-eksempler, før man skriver koden. Det giver flere fordele. Når man er tvunget til at definere sine forventede slutresultater, vil man ofte blive opmærksom på kompleksitet eller sammenhænge, man ikke havde tænkt over tidligere. Det kan stille nye krav til den endelige kode. Når man har defineret slutproduktet, er det samtidigt muligt at kunne vurdere, hvor langt projektet er kommet, som koden bliver færdig. Download FUTS FUTS kan downloades gratis fra den amerikanske webside thotwawe.com, hvis man lader sig registre. Du finder også dokumentation og artikler om FUTS her. http://www.thotwave.com/products/futs Den downloadede fil indeholder en række mappe med diverse filer. Konfigurer SAS, så makroerne indlæses automatisk hver gang Du kan få din lokale SAS-klient til at indlæse makroerne fra FUTS automatisk ved opstart af din SAS-session. Det kan ske med en tilføjelse i den lokale CFGfil, som indlæses hver gang SAS starter op. Uddrag fra cfg-filen fra SAS 9.1.3 /* Setup the SAS System autocall library definition */ -SET SASAUTOS ( "C:\Applikation\FUTS\futs\macros" "!sasroot\core\sasmacro" "!sasext0\dmine\sasmacro" "!sasext0\access\sasmacro" "!sasext0\assist\sasmacro" "!sasext0\eis\sasmacro" "!sasext0\ets\sasmacro" "!sasext0\gis\sasmacro" "!sasext0\graph\sasmacro" "!sasext0\or\sasmacro" "!sasext0\qc\sasmacro" "!sasext0\share\sasmacro" "!sasext0\stat\sasmacro") Du finder filen i din lokale installationsmappe for SAS under mapperne nls/en.
Hvad kan makroerne i FUTS De 24 makroer I FUTS (version 1.1) kan deles op i tre hovedgrupper. Den første arbejder med problematikken med, hvorvidt vores SAS-data eller eksterne filer eksisterer eller ej. Den anden gruppe sammenligner et specifikt datasæt før og efter en programændring. Det kan dreje sig om dele af datasættene eller hele datasættet. Den sidste gruppe handler om sammenligning af resultater, som er lavet med bearbejdninger af data før og efter programændring. Det kan f.eks. være, at man har beregnet et gennemsnit med Proc Summary før og efter en kodeændring og ønsker at sammenligne resultatet. Makronavn Kilde: FUTS dokumentation Beskrivelse af makroens indhold 1 %assert_compare_equal Generates an event if two data sets are not the same, as compared by PROC COMPARE. 2 %assert_empty Generates an event if a data set has any observations; no event if data set does not exist. 3 %assert_equal Generates an event if two arguments are not equal. 4 %assert_exist Generates an event if a data set does not exist. 5 %assert_fexist Generates an event if a file identified by fileref does not exist. 6 %assert_filecompare_equal Generates an event if two text files identified by name are not 7 %assert_fileexist equal. Generates an event if a file identified by name does not exist. 8 %assert_not_compare_equal Generates an event if two data sets are the same, as compared by PROC COMPARE. 9 %assert_not_empty Generates an event if a data set has no observations, or if the data set does not exist. 10 %assert_not_equal Generates an event if two arguments are equal. 11 %assert_not_exist Generates an event if a data set does exist. 12 %assert_not_fexist Generates an event if a file identified by fileref exists. 13 %assert_not_fileexist Generates an event if a file identified by name exists. 14 %assert_not_null Generates an event if the argument is null (has zero length). 15 %assert_not_zero Generates an event if the argument is zero. 16 %assert_null Generates an event if the argument is not null (has non-zero 17 %assert_sym_compare length). Generates an event if the arguments do not meet the comparison criteria. 18 %assert_zero Generates an event if the argument is non-zero. 19 %exist Verifies the existence of a SAS data set. 20 %expect_error Registers an expected error type, so that an assertion event will not generate a notification. 21 %fexist Verifies the existence of a file by fileref. 22 %fileexist Verifies the existence of a file by name. 23 %generate_event Generates a notification that an assertion has failed. 24 %obs Returns the observation count for a data set.
Alle makroerne har en lang række forskellige valgmuligheder, som man kan bruge i sine test-senarioer afhængigt af situationen. Der står nærmere beskrevet i toppen af hver enkel makro. Biblioteksnavne- og referencer For at undgå en konstant omdøbning af datanavne eller ændring af biblioteksreferencer, er det en fordel at bruge makro-referencer i stedet for hardkodet libname. På den måde kan du skiftevis kører programmerne med og uden ændringer og få outputet over i de rigtige biblioteksmapper. Testen %assert_compare_equal undersøger om de fire andre variable i datasættet er blevet påvirket af ændringen af variablen age. Vores kodeeksempel er meget enkelt, men i en mere kompleks kode kunne der sagtens være afledt effekt, når man ændrer en variabel. %assert_sym_compare sammenligner en bearbejdet værdi fra datasættet med en fast værdi. Den sidste test %assert_zero undersøger loggen for errors, hvilket bør være standard. Vi kunne også have valgt warnings i stedet for, da ingen af delene bør optræde i loggen. Det simple eksempel I det første eksempel bruges der et datasæt, hvor en enkelt variabel bliver ændret i et simpelt datastep og hvor outputtet gemmes i saswork. Data work.class; set sashelp.class; age = age + 2.0; run; Som datagrundlag bruges datasættet Class, som du finder i SAS-blioteket sashelp. Det har 19 observationer og fem variable. Det lille datasæt viser fint pointerne og resultaerne er de samme, som hvis vi havde haft 1500 variable og 10. millioner observationer. Den eneste forskel er, at vi nemt kan overskue datasættet. Den første test %assert_exist kontroller om datasættet Class findes i work. Hvis ikke, er optionen abort tilvalgt. Det betyder, at SASsessioner afbryder, hvis der ikke findes noget data, da de efterfølgende tests bliver irelevante. Testen %assert_not_empty undersøger om data har nogle observationer eller om datasættet er tomt. 1. eksempel på simpel anvendelse af FUTS %** Trin 1: Undersøger om datasættet findes **; %assert_exist( work.class, level=error, type=table_missing, abort=yes %** Trin 2: Har data nogle observationer **; %assert_not_empty ( work.class, type=table_empty, level=error, abort=yes %** Trin 3: Er de ikke-manipulerede variable er påvirket af den nye kode? **; %assert_compare_equal( base=sashelp.class, compare=work.class;, var=name sex height weight, message=assert_compare_equal: Datasættene er ikke ens %** Trin 4: Har den nye kode ændret variablen age **; %assert_not_compare_equal( base=sashelp.class, compare=work.class;, var=age, message=assert_not_compare_equal: datasættene er ikke ens %** Trin 5: check af værdien af en given beregning af den maipulerede variable **; proc summary data=work.class; var age; output out=age_meantotal mean=mean_age; run;
%** Trin 6: test af registerede ERRORS fra fejlmakroen &syserr fra proc summary **; %assert_zero( &SYSERR, level=error, message=assert_zero: procedure exited with errors, type=proc_error %** Trin 7: værdien puttes ind i en makro **; data _null_; set age_sumtotal; call symput('mean_age',mean_age stop; run; %** Trin 8: test af den forventede værdi før og efter **; %let test=14.315789474; %assert_sym_compare( &test., level=note, PRECISION=1e-15, message=assert_sym_compare: programmet har ændret gennemsnitsalderen!, type=comparison, abort=no, operator=eq Det avancerede eksempel med Perl Når man arbejder med FUTS, vil antallet af testrutiner ofte vokse. Så kan det være en god ide at skifte til et mere avanceret setup, som inddrager programmet Perl. Det kan bruges til at afvikle mange små testprogrammer og er meget velegnet til batchafvikling. Perl er et freeware program (læs betingelserne på deres side), som kan downloades fra websiden (http://www.activestate.com/activeperl/downl oads). For at få SAS og Perl til at virke sammen, skal der tilføjes to små opsætningsfiler, som placeres i roden af dit lokale FUTS-bibliotek og som skal kalde hinanden. FUTS.CMD => setenviroment.cmd Opsætningsfilen FUTS.CMD setlocal call setenvironment.cmd echo Clear output folder from last run... if not exist %SAS_APPLICATION_ROOT%\sasforum\output mkdir %SAS_APPLICATION_ROOT%\sas-forum\output del /Q %SAS_APPLICATION_ROOT%\sasforum\output\*.* echo Delete *.LOG files from last run... del /Q /S %SAS_APPLICATION_ROOT%\*.log > NUL: if '%1' equ '' (echo Run tests in all FUTS directories... futs.pl -ek %SAS_APPLICATION_ROOT% ) else ( echo Run the specified test... futs.pl -ek %1 ) endlocal pause Opsætningfilen setenvironment.cmd set SAS_APPLICATION_ROOT=C:\FUTS\SAS-forum set SASROOT=C:\Programmer\SAS\SAS 9.1 set SASOPTS=-sasUser %temp% -rsasuser - sasinitialfolder="." -echoauto De to opsætningsfiler, som ses ovenover, har oplysninger om, hvor der skal testes, hvor SAS er placeret, biblioteksreferencer og specielle SAS options. Afviklingen via Perl sker via en kommandofil (*.cmd), hvor man kalder Perl (futs.pl) og samtidigt fortæller, hvor testmappen er placeret. Når man afvikler sine FUTS-test via Perl, bliver alle filer med *.sas afviklet på nær filerne setup.sas og teardown.sas. Setup.sas bruges til at indlæse libnames eller andre ting, som skal bruges FØR testene afvikles. Teardown.sas læses til sidst og kan f.eks. ryde op inden testen afsluttes. Via DOS-vinduet (eller logfilen fra kørslen) kan man se, hvor mange tests, der afvikles (Tests executed) og hvor mange der fejlede (tests failed).
Går alle testene fint, fortæller den sidste linie, at de samlede test er GRØNNE. Hvis der har været fejl, bliver testene markeret som RØDE. Så selvfølgelig kan det betale sig at bruge tiden på FUTS! Chefkonsulent Søren Jessen Region Hovedstaden (Koncern HR) sj@regionh.dk www.regionh.dk Opsummering Er det tiden værd at indarbejde FUTS i sine arbejdsrutiner, når man programmer? Testene bidrager til at få kontinuitet og kvalitet i arbejdet. Samtidigt husker testene alle specialtilfælde som ingen andre kan huske. Selv om man har startet med at definere en række test inden man begyndte at kode, vil der ofte komme nye tests til. Man kan sige at efterhånden som man gør sig nye erfaringer med den aktuelle analyse- og datamodel, vil antallet (og kvaliteten) af testene stige. Kilder: Noter fra SAS (Danmark) kursus om FUTS med Alex Zilmer. SASUnit: Automated Testing for SAS Greg Barnes Nelson, President and CEO ThotWave Technologies, LLC. Cary, NC Drawkcab Gnimmargorp: Test-Driven Development with FUTS Jeff Wright, ThotWave Technologies LLC, Cary, NC Testrutinerne gør at man ikke skal starte forfra hver gang man arbejder med en programproblematik.