niedziela, 18 marca 2012

"Teoria mostów" czyli dlaczego programiści mylą testy jednostkowe z integracyjnymi

Na początek quiz : po jakim czasie programiści zaczynają z coraz większą częstością stosować dobrze znane polecenie?


-Dmaven.test.skip=true

Prowadząc osobiste obserwacje zauważyłem, że jednym z czynników jaki ma wpływ na ów niechlubny krok jest czas wykonania się testów jednostkowych. Jeśli do kogoś bardziej przemawiają zapisy ścisłe to można powyższe stwierdzenie ująć TAK:


P(s)~Tc

gdzie:
  • P(s) - prawdopodobieństwo przeskoczenia kroku testów
  • Tc - czas wykonania się testów

lub TAK:

f(s)~Tc

gdzie:
  • f(s) - częstotliwość olewania testów
  • Tc - czas wykonania się testów

Wniosek jest oczywisty : wśród rzeczy które w naszym codziennym życiu chcemy sobie wydłużać nie powinno być testów jednostkowych!

Dlaczego programiści mylą testy jednostkowe z integracyjnymi


Oczywiście nie wszyscy programiści i na pewno nie ci którzy czytają tego bloga. Niestety świat zewnętrzny jest bezlitosny i nie raz widziałem jak coś co było w danej firmie nazywane "testami jednostkowymi" stawiało cały serwer aplikacji. Źródeł podobnych kroków dopatruję się w fakcie, iż tak naprawdę nie ma (niestety) ścisłej definicji testu jednostkowego. No i różni ludzie różnie rozumieją ową "jednostkę".

Zamiast ścisłej definicji zafundujmy sobie wolne i pozbawione ciśnień rozważania przy pomocy

prostego przykładu (z użyciem pseudo kodu)


 
 int countLines(pathToFile){
    File fileWithContent=new File(pathToFile);  
    String content=readContent( fileWithContent)  
    return countLines(content) 
 } 

Powyższy kod przedstawia kawałek logiki zliczającej ilość linii w pliku. Stworzenie testu jednostkowego dla tego kawałka kodu wydaje się proste.Niechże wygląda on tak:


 //given  
 pathToTestFile="..."  
 //when  
 int result=linesCounter.countLines(pathToTestFile)  
 //then  
 assertThat(result, is(tyleATyle))  

Dlaczego to nie jest test jednostkowy?


Aby odpowiedzieć na to pytanie wystarczy w dowolny sposób popsuć plik testowy. I tutaj niestety jest miejsce na filozoficzne dywagacje czy plik jest częścią testowanej jednostki czy też nie. Według mnie nie jest. Jako jednostkę rozumiem logikę znajdującą się w kontekście testowanego kawałka kodu. Usuwając plik testowy nie zmieniamy nic w samym komponencie ani w naszym teście a jednak jesteśmy w stanie zmienić kolor paska rezultatu w JUnicie. Moim zdaniem można śmiało powiedzieć, że nasz test poza liczeniem linii testuje integrację z systemem plików!


Mosty i "Kontektory"



Idea przedstawiona na rysunku jest chyba oczywista. Do każdego zewnętrznego systemu odwołujemy się poprzez specjalny zbiór łączników zwanych z angielska "connectorami" (chciałbym dożyć chwili gdy będąc w samym centrum tego pięknego kraju będę mógł ochoczo nazwać sobie klasę FileSystemŁącznik). Na początku używałem jednego kontektora dla jednego systemu ale skutkowało to rozrostem jednej ogromnej klasy. Najnowsza teoria grupuje kontektory w mosty.


Jak to może wyglądać w praktyce


Widok od strony pakietów


i przykładowy kod źródłowy

 public class TextResourcesConnector {  
      public String readTextResourceContent(String resourceName) {  
           InputSupplier<InputStream> contentSupplier = newInputStreamSupplier(getResource(resourceName));  
           return readContent(contentSupplier);  
      }  
      public String readTextFromFile(File file){  
           InputSupplier<? extends InputStream> contentSupplier =Files.newInputStreamSupplier(file);  
           return readContent(contentSupplier);  
      }  
      public String readTextFromFile(String filePath){  
           return readTextFromFile(new File(filePath));  
      }  
      private String readContent(InputSupplier<? extends InputStream> contentSupplier) {  
           try {  
                return CharStreams.toString(newReaderSupplier(contentSupplier, Charsets.UTF_8));  
           } catch (IOException e) {  
                throw new RuntimeException(e);  
           }  
      }  
 }  

Powyższy kod przedstawia wersję jednego z kontektorów w kształcie odpowiednim na chwilę obecną (co bardziej spotrzegawczy zauważą wykorzystanie Guavy). Koncepcja ewoluuje, więc nie wykluczone, że w przyszłość całość będzie wyglądać inaczej. Kod naszego "zliczacza linii" wyglądałby teraz następująco


 
 int countLines(pathToFile){ 
    String content=textResourcesConnector.readContent( fileWithContent)  
    return countLines(content) 
 } 

Zaś test :

 

 before(){
   textResourcesConnector=mock()
   linesCounter.wstrzyknijTakLubInaczej(textResourcesConnector)
 }

 (...)

 //given  
 pathToTestFile="..."
 content="..."
 given(textResourcesConnector.readContent(pathToTestFile)).willReturn(content);
 //when  
 int result=linesCounter.countLines(pathToTestFile)  
 //then  
 assertThat(result, is(tyleATyle))  

Test co prawda nam się powiększył objętościowy ale to dlatego, że teraz mamy pełną kontrolę nad tym co dzieje się na granicy systemu i z kontroli tej (a jakże) korzystamy. Oczywiście testowanie samych kontektorów to osobna sprawa - tutaj raczej przed testem integracyjnym się nie ucieknie.ALE!


Akapit-wniosek artykułu


Testy integracyjne poza tym, że są mniej przewidywalne to zazwyczaj trwają dłużej z powodu komunikacji dwóch systemów. Także aby ludzie nie "skipowali" testów to te integracyjne powinny być uruchamiane osobno od jednostkowych. W tej chwili osobiście używam mechanizmu Springowego

@IfProfileValue(name="test.type", value="integration")

Dzięki czemu owe testy integracyjne nie będą się uruchamiały razem z jednostkowymi. (Podobno maven 3 ma jakaś osobną fazę poświęconą testom integracyjnym ale ja jeszcze tego nie znaju.) Teraz gdy już mamy naszą annotację nad testami integracyjnymi to możemy stworzyć osobny job w jenkinsie, który raz na jakiś czas będzie je uruchamiał a programiści mogą radośnie delektować się (relatywnie) szybkim wykonaniem instukcji mvn clean install.

Ps. Do testowania operacji na systemie plików polecam w miarę młody mechanizm w JUnit

@Rule
public TemporaryFolder tmpFolder = new TemporaryFolder();


niedziela, 4 marca 2012

Umysł próżny

"Failure is perhaps the greates enemy of the Ego"

Z raportów policyjnych


  • "Facet był na całej drodze i wiele razy próbowałem go wyminąć zanim uderzyłem w niego"
  • "Wracając do domu podjechałem do złego domu i zderzyłem się z drzewem którego nie mam."
  • "Przechodzień uderzył we mnie i przeszedł pod moim samochodem."
  • "Ten pan jest bezstronnym świadkiem na moją korzyść."
  • "Budka telefoniczna zbliżała się, kiedy próbowałem zjechać jej z drogi uderzyła w mój przód."
  • "Zatrąbiłam na pieszego, ale on tylko się na mnie gapił, więc go przejechałam."
  • "W pewnym momencie drzewo dostało się między kabinę ciężarówki i przyczepę."
  • "Jechałem sobie spokojnie, a tu nagle zaatakowała mnie wysepka tramwajowa."


Czytając tych kilka śmiesznych ( i podobno prawdziwych) fragmentów z raportów policyjnych trudno nie oprzeć się wrażeniu, iż ich autorzy w niezwykle naiwny sposób próbują uciec przed odpowiedzialnością. A być może nie chodzi tutaj o odpowiedzialność a o pielęgnowanie wizji siebie jako doskonałego kierowcy?

Powrócę tutaj do tematu, który poruszyłem w odcinku Dlaczego się nie uczymy
Jeśli komuś nie chcę się (znowu) tego czytać to w skrócie chodziło o to, że w nauce dowolnej umiejętności najtrudniejszy jest krok przejścia od "niekompetencji nieświadomej" do "niekompetencji świadomej" czyli zwyczajny przebłysk zatytułowany "chyba ja tego ku*wa jednak nie umiem!".


Dziś nadszedł czas aby bliżej przyjrzeć się w jaki sposób nasz umysł buduje tarczę obronną przed świadomością iż "my jako ludzie jesteśmy jedynie kruchymi i przemijającymi istotami połączonymi we wspólnej walce o przetrwanie w bezcelowym wszechświecie jedynie po to aby umrzeć i przeminąć" ;)


"Self-candicapping"


"Self-candicapping" czy też z polska "samo upośledzanie"(?) można dosyć łatwo wytłumaczyć. Zapewne nie jeden z was słyszał w szkole opinię nauczycielki o sobie czy kimś znajomym "on jest zdolny ale leniwy". Self-candicapping upraszcza tę sytuację i usuwa z przykładu nauczycielkę : "Jestem zajebiście zdolny ale leniwy. Gdybym tylko chciał to bym wszystko rozjebał no ale mi się nie chce".
Ale żeby nie pie*dolić farmazonów przejdźmy do badań naukowych. Tam gdzie mi się będzie chciało będę podawał źródła.


Badanie - test na inteligencję

Studenci zostali podzieleni na dwie grupy. Eksperyment polegał na rozwiązaniu niezwykle trudnego dwuczęściowego testu na inteligencję. W trakcie przerwy pomiędzy pierwszą a drugą częścią studenci byli pytani o swój poziom zdenerwowania i o to jak duży wysiłek wkładają w rozwiązanie testu. I tutaj pojawia się meritum eksperymentu: otóż jednej z grup "zabrano" jedną z wymówek i zakomunikowano, iż test został tak przygotowany aby bez względu na poziom zdenerwowania dać właściwy obraz inteligencji studenta.


Wyniki:

Grupa która mogła wybrać sobie swój "handicap" postawiła na zdenerwowanie (być może jako bardziej naturalne) i określiła iż znajdowała się pod wysokim wpływem tego uczucia. Druga grupa nie mogła zgonić winy za złe wyniki na zdenerwowanie toteż w tej grupie okazało się, że studenci użyli drugiej metryki i określili swój wysiłek włożony w rozwiązanie testu jako niewielki.


Źródło : Smith T.W. 1982 On the self serving function of an academic wooden leg : Test anxiety as self-handicaping strategy.

"Self-Shifting"


Pamięć - największy sprzymierzeniec EGO. Kiedy twoja, czy też dla bezpieczeństwa "czyjaś" wizja siebie przestaje pokrywać się z aktualnymi potrzebami pamięć zaczyna tasować wspomnieniami tak aby znaleźć coś wygodniejszego.


Badanie - cecha prowadząca do sukcesu

Znowu mamy dwie grupy losowo wybranych studentów. Jedna grupa dostaje artykuł twierdzący, że osoby ekstrawertyczne odnoszą większe sukcesy po zakończeniu szkoły, druga grupa zaś dla przeciwieństwa dostaje artykuł chwalący introwertyzm.


Wyniki:

Bez niespodzianek - u studentów pierwszej grupy mózg zamówił z magazynu pamięci wspomnienia potwierdzające, że są ekstrawertykami zaś w przypadku drugiej grypy potrzebne były wspomnienia świadczące o czymś przeciwnym.


Źródło : Kunda Z. and R. Sanitioso 1989 Motivated changes in the self-concept .

Trust (próżnych) mózgów





Istnieje ciekawa metafora naszego umysłu. Jest to prawnik, który nie szuka prawdy ale dowodów na obronę swojego klienta czyli nas. Ma on do pomocy naszą pamięć czyli sekretarkę, która pomaga mu filtrować wszystkie dowody w poszukiwaniu tego, który dowiedzie niewinności klienta. Jak to w sądzie mamy także świadków. A najlepsi świadkowie to oczywiście ci "bezstronni na naszą korzyść". Istnieje badanie pokazujące jak bardzo nasz umysł dąży do tego aby przebywać w towarzystwie ludzi, którzy będą pielęgnować nasze złudzenia.

Dam tutaj nawet linka do tych badań ze względu na swojsko brzmiące nazwisko :
In Search of Information That Confirms a Desired Self-Perception: Motivated Processing of Social Feedback and Choice of Social Interactions by Wlodarski and Sanitioso


Przypomina mi to anegdotke, którą przeczytałem w jednej z książek Bandlera (współtwórcy NLP - podobno). W trakcie lekcji pierwszej nauczył studentów rozpoznawać ludzkie fobie i słabości. Lekcja druga polegała na tym aby nie robić tego u swoich znajomych...


Jeszcze jeden ciekawy przykład


Miało miejsce jeszcze jedno ciekawe badanie, które pokazało, iż nasz umysł jest w stanie nawet zmienić fizyczną tolerancję na chłód. Standardowo jak to w takich badaniach podzielono studentów na dwie grupy gdzie jednej wkręcono istnienie badań dowodzących, że oznaką długowieczności jest odporność na zimno - zaś grupie drugiej coś przeciwnego. Łatwo chyba przewidzieć, uczestnicy której grupy byli w stanie dłużej trzymać rękę w kuble wypełnionym lodem.


Czy ten mechanizm jest opłacalny

Nie w dzisiejszych czasach. Znowu jest eksperyment i znowu dwie grupy ale tym razem maklerów giełdowych grających na wirtualnej wersji giełdy z losowymi zmianami. Maklerzy, którzy wkręcili sobie, iż posiadają wpływ na symulację zarabiali średnio 100 000$ mniej od tych skromniejszych. (Fenton - O'creny Trading on illusions Unrealistic perceptions of control and trading performance.)


Okno rzeczywistości


Według badaczy mózg posiada jedną technikę obrony przed nim samym. Tzw. "Okno rzeczywistości". NA chwilę przed podjęciem decyzji - niech będzie to zakup samochodu - załącza się u nas bardziej profesjonalne zarządzanie ryzykiem i przygasa trochę omnipotencja. Jednakże chwilę po podjęciu decyzji wspomniane okno zamyka się z trzaskiem! To tutaj pełne pole do popisu ma dysonans poznawczy i racjonalizacja właściwego wyboru.


Moje techniki




Obecnie najsilniejszą znaną mi techniką odnośnie wpływania na własne przekonania jest medytacja połączona z wizualizacją. Jest to jednocześnie technika wymagająca pewnej wiedzy teoretycznej i poświęcenia czasu na ćwiczenia. Ostatnio spróbowałem z czymś łatwiejszym co można by nazwać realną wizualizacją na jawie (nie Javie). Generalnie polega to na skontruowaniu swojego pseudo alter-ego w potaci profilu na forum dyskusyjnym i działaniu na dwa sposoby

  • Mini trolling - ponieważ to nie nasze ego będzie atakowane toteż nie powinny wystąpić reakcje obronne. Jednocześnie nasz umysł powinien być w stanie wyłapać argumenty za i przeciw danego przekonania.
  • Trolling przeciwstawny - tutaj trollujemy na rzecz argumentu z którym się nie zgadzamy (czyli np "Od czego najlepiej zacząć naukę PHP po porzuceniu javy") i wtedy nasz umysł już kompletnie będzie zakręcony co pozwoli na wygodne metaprogramowanie


Na koniec plusy dodatnie. W innym eksperymencie studenci pierwszego roku zostali wkręceni, iż po zakończeniu pierwszego semestru ich oceny podskoczą w górę. Młodzi tak w to uwierzyli, że zapierdalali aż ich wkręcone przekonania się urzeczywistniły...