Category Archives: Tests

Jakou jistotu máte vy?

Bez ohledu na to, kolik bílých labutí jsme pozorovali, není oprávněný názor, že všechny labutě jsou bílé.

Také jste si všimli, jak v programování řešíme stejné problémy jako řeší vědátoři? Jasně, nejde si toho nevšimnout. Ale i tak to tu napíšu.

Věda se dá dělat několika způsoby. První je intuitivní. Prostě něco pozoruji, z pozorování vyvodím nějaké závěry a těm věřím. Moc se nezatěžuji experimenty, spíš se snažím používat hlavu a z konkrétních pozorování dělat obecné závěry. Všechny labutě, které jsem kdy viděl byly bílé, takže je jasné, že jsou všechny labutě bílé.

Tuto metodu můžu výrazně vylepšit tím, že se spojím s ostatními, předvedu jim svoje výsledky a nechám je tam hledat chyby. To je obrovský krok vpřed. Ostatní můžou najít moje chyby v logice a napravit je. Dokonce mohli vidět i něco, co jsem já předtím neviděl. Třeba dokonce někdo viděl modrou labuť?

Třetí krok je, že se snažím dělat systematické pokusy, které mi o skutečnosti řeknou mnohem víc než nahodilá pozorování. Záměrně ten jev pozoruji v různých podmínkách a snažím se ho pochopit hlouběji. Fyzika s tímto přístupem začala před pár set lety, ostatní vědy se postupně přidávaly a těch pár zbývajících se na to už pomalu chystá.

Ale je tu ještě čtvrtý krok, který zatím udělalo jen pár oborů. Nesnaží se jenom dělat pokusy, které jejich hypotézy potvrdí. Dělají pokusy, které je mají vyvrátit. Záměrně se snaží najít si na své víře chyby, snaží se vymyslet pokus, který jejich teorii rozbije. Snaží se najít labuť jiné barvy. Čím víc takových pokusů udělají, tím je ta hypotéza pravděpodobnější (i když jistoty nikdy nedosáhnou).

Čím dál daný obor je v tomto žebříčku, tím jsou jeho předpovědi užitečnější. Věříte víc předpovědím fyziků nebo ekonomů? Tím nechci říci, že by obory, které se nedostaly tak daleko jako fyzika, byly nějak retardované. Z velké části je to tím, že je mnohem snazší tisíckrát pustit dělovou kouli z věže, než nasimulovat chování celého státu nebo dokonce planety. Ale ať už je příčina jakákoliv, obory které se s pokusy dostaly dál, mají mnohem přesnější předpovědi.

Stejné je to i s programováním. Máme intuitivní přístup. Napíši kus kódu, spustím ho a párkrát do něj kliknu. Moc nepátrám jestli funguje. Jde to zkompilovat, vypadá to, že to funguje, tak to asi funguje. Navíc jsem to napsal já, takže to určitě bude dobře.

Toto se dá vylepšit za pomoci review. Kód ukážu někomu jinému, a on mi tam určitě najde nějaké chyby. Nicméně je to podobné jako když si ekonomové navzájem píší recenze. Dokud se neudělá pokus, tak je to jen výplod fantazie. Ano, i když vám review dělá nejlepší expert ve firmě, o výsledné funkčnosti to má stejnou vypovídající hodnotu, jako když vám recenzi napíše nejlepší ekonom na světě.

Jediné co má vypovídající hodnotu jsou testy. Myslíte si, že vaše aplikace funguje tak jak má? Dokud to systematicky nevyzkoušíte, tak je to jen výplod vaší fantazie a zbožné přání. Důležité je to slovo systematicky. Musíte se zamyslet nad tím jaké podmínky ten test můžou ovlivňovat, a všechny je vyzkoušet, pěkně jednu po druhé. Nicméně tu stále hrozí to, že se budete snažit tu svoji hypotézu jen potvrdit. Budete pořád chodit k tomu samému rybníku a pořád tam uvidíte jenom bílé labutě. Budete pořád procházet jenom základní scénáře, zkoušet to co zkoušíte vždycky. I to je veliký pokrok oproti předchozím variantám, ale je tu velké riziko zaslepenosti.

Pokud se totiž vědomě nepokoušíte hypotézu vyvrátit, tak hrozí, že budete nepříjemná fakta ignorovat. Možná ne záměrně, ale náš mozek je tak stavěný. Co se mu nehodí, to jednoduše nevnímá. Je proto potřeba změnit způsob myšlení a snažit se aktivně naše hypotézy vyvracet. Věříte, že máte aplikaci dobře zabezpečenou? Dokud se do ní pravidelně a systematicky nepokoušíte prolomit, tak je to zase spíš jen zbožné přání než realita. Věříte, že váš systém utáhne tu zátěž jakou potřebujete? A zkusili jste si to vyvrátit?

Oproti vědcům máme tu výhodu, že je software obvykle snadno testovatelný. Dá se jednoduše nasimulovat skoro cokoliv potřebujete. Takže nemáte stejnou výmluvu jakou mají všichni ti klimatologové, psychologové nebo ekonomové. Vy pokusy dělat můžete.

Takže je jen na vás, jestli chcete mít stejnou spolehlivost jako ekonomové nebo jestli by se vám spíš líbilo mít stejně spolehlivé předpovědi jako fyzici. Záleží jen na volbě metody, kterou si vyberete.


Jako malé bonusové cvičení tu mám různé hypotézy, které pravděpodobně máte kolem vaší aplikace. Zkuste jim přiřadit body, podle toho jak moc systematicky ověřujete jejich správnost. Jeden bod je za intuitivní přístup, dva za to samé plus review, tři za systematické testy a čtyři za systematické negativní testy.

  1. K našim datům se nikdo nepovolaný nedostane.
  2. Naše aplikace má správnou architekturu.
  3. UI je přehledné a pro zákazníka snadno pochopitelné.
  4. Nemáme tam žádnou chybu.
  5. Naše aplikace utáhne zátěž jakou potřebujeme.
  6. Produkční konfigurace je správně.
  7. Když dám všechny komponenty dohromady, tak to bude fungovat.
  8. Když udělám změnu, tak bude všechno výše uvedené i nadále platit.
  9. Blikající nadpisy jsou nejlepší na světě.
  10. Stihneme to naimplementovat včas.
  11. Ten nový proces zvýší naší produktivitu.

Tak co, kolik máte čtyřek? Já moc ne. A kolik jedniček? Víc než je zdrávo co? Tak co s tím uděláme?

Zdroje: Problémy popperovské falsifikace

Pokud jde kód špatně otestovat, je špatně navržený.

Píšete unit testy? Pokud ano, přemýšleli jste nad tím co vám přináší? Já ano a čím dál tím víc začínám zastávat názor, že unit testy tu nejsou od toho, aby hledaly chyby v kódu.

Účelem unit testů je totiž hledání chyb v návrhu. Je to takový rychlý detektor. Zkusíte napsat unit test na kus kódu a když to jde špatně, tak je ten kód špatně navržený. Tečka. Platí totiž jednoduché pravidlo:

Pokud jde kód špatně otestovat, je špatně navržený.

Nevím jak bych vám to dokázal, ale když se nad tím zamyslíte, tak je to evidentní. Když mám stořádkovou metodu, tak přeci nemůžu napsat jednoduchý test, který pořádně otestuje všech sto řádků. Pokud mám třídu, která dělá několik věcí současně, tak ani nebudu vědět, kde s testováním začít. Na a když mám třídu, která má hromadu závislostí, tak se zblázním z mockování.

Takže pokud vám připadá, že psaní a udržování unit testů je hromada ošklivé práce, tak není chyba v testech, ale v kódu, který se snažíte testovat.

Jediné co vám mohu doporučit je TDD. Když začnu psát testy před kódem, tak dojde k takovému kouzlu. Nebál bych se použít i slovo zázrak. I patlal jako já najednou začne psát jasný a jednoduchý kód. Když procházím svůj starší kód, na první pohled poznám, který kód jsem psal pomocí TDD a který klasicky. Když mám testy hotové před tím, než začnu kazit kód, tak mě udrží v mezích a nenechají mě překomplikovat návrh a psát špagetykód. Když si nechávám testy na potom, tak je ten kód prostě mnohem ošklivější. A to je podle mě největší přínos unit testů. Žádné hledání chyb, ale nástroj, který vás dovede ke správnému návrhu.

Takže, pokud už to tedy dávno neděláte, tak opravdu zkuste psát testy před kódem. Je to kouzlo, které když nezkusíte tak o hodně přijdete.

Poznámka: Tento blog jsem napsal proto, že jsem hledal něco podobného v češtině a nic nenašel. Přišlo mi to škoda. Jsou i lidé, kteří tvrdí, že TDD je největší změna v našem odvětví za posledních deset let. Nebo tak nějak (nedaří se mi najít zdroj). Jinak pěkný článek o tom k čemu jsou unit testy najdete tady.

Při hledání podkladů, jsem narazil na tento svůj zápisek a hlavě dokument z něj odlinkovaný, na který jsem úplně zapomněl a který mě úžasně pobavil. Doporučuji.

Mock socket

Few months ago I have written a small tool that mocks network sockets in Java. Now I have some time to describe it, so here you are.

Let’s imagine you want to test network communication in Java. It’s not easy, you have to start some server on the other side, configure its responses and somehow verify that the data you send are correct.

With mock-socket it’s incredibly easy.

import static net.javacrumbs.mocksocket.MockSocket.*;
...

//prepare mock
byte[] dataToWrite = new byte[]{5,4,3,2};
expectCall().andReturn(emptyResponse());
	
//do test
Socket socket = SocketFactory.getDefault().createSocket("example.org", 1234);
IOUtils.write(dataToWrite, socket.getOutputStream());
socket.close();
	
//verify data sent
assertThat(recordedConnections().get(0), data(is(dataToWrite)));
assertThat(recordedConnections().get(0), address(is("example.org:1234")));

You see, just statically import MockSocket class, prepare the mock, execute the test and verify the data. The library just removes the default Java socket implementation and place a mock implementation in its stead.

Of course, this example does not have much sense. It just tests that Java socket implementation works. But imagine that you implement some non-trivial network library. A test library can be handy.

Moreover, there is a HTTP extension which can be used if you want to test some HTTP client. Let’s say a JSON REST client. In such case, you can write this.

import static net.javacrumbs.mocksocket.http.HttpMockSocket.*;

...

//prepare mock
expectCall()
  .andWhenRequest(uri(is("/test/something.do")))
     .thenReturn(response().withStatus(404))
  .andWhenRequest(uri(is("/test/other.do")))
    .thenReturn(response().withContent("Text")).thenReturn(response().withContent("Text"));

//do your test
...

//verify 
assertThat(recordedConnections(), hasItem(header("Accept", is("text/plain"))));		

Ain’t great? You can do much more, please take a look at the project page if you are interested.