<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/atom10polishfull.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:openSearch="http://a9.com/-/spec/opensearch/1.1/" xmlns:georss="http://www.georss.org/georss" xmlns:thr="http://purl.org/syndication/thread/1.0" xmlns:gd="http://schemas.google.com/g/2005" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" gd:etag="W/&quot;C0UNQHcyeSp7ImA9Wx5QF0o.&quot;"><id>tag:blogger.com,1999:blog-5009385396224025678</id><updated>2010-09-06T13:08:11.991+02:00</updated><title>Studio Pragmatists</title><subtitle type="html">co robimy, jak tworzymy oprogramowanie, co jest dla nas ważne</subtitle><link rel="http://schemas.google.com/g/2005#feed" type="application/atom+xml" href="http://studiopragmatists.blogspot.com/feeds/posts/default" /><link rel="alternate" type="text/html" href="http://studiopragmatists.blogspot.com/" /><author><name>Pragmatists</name><uri>http://www.blogger.com/profile/02774380699386691980</uri><email>contact@pragmatists.pl</email></author><generator version="7.00" uri="http://www.blogger.com">Blogger</generator><openSearch:totalResults>23</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/atom+xml" href="http://feeds.feedburner.com/StudioPragmatists" /><feedburner:info uri="studiopragmatists" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><feedburner:feedFlare href="http://add.my.yahoo.com/rss?url=http%3A%2F%2Ffeeds.feedburner.com%2FStudioPragmatists" src="http://us.i1.yimg.com/us.yimg.com/i/us/my/addtomyyahoo4.gif">Subscribe with My Yahoo!</feedburner:feedFlare><feedburner:feedFlare href="http://www.newsgator.com/ngs/subscriber/subext.aspx?url=http%3A%2F%2Ffeeds.feedburner.com%2FStudioPragmatists" src="http://www.newsgator.com/images/ngsub1.gif">Zapisz w NewsGator Online</feedburner:feedFlare><feedburner:feedFlare href="http://client.pluck.com/pluckit/prompt.aspx?GCID=C12286x053&amp;a=http%3A%2F%2Ffeeds.feedburner.com%2FStudioPragmatists" src="http://www.pluck.com/images/rss-pluck.gif">Zapisz w czytniku Pluck RSS</feedburner:feedFlare><feedburner:feedFlare href="http://feeds.my.aol.com/add.jsp?url=http%3A%2F%2Ffeeds.feedburner.com%2FStudioPragmatists" src="http://o.aolcdn.com/myfeeds/html/vis/myaol_cta1.gif">Zapisz w My AOL</feedburner:feedFlare><feedburner:feedFlare href="http://www.bloglines.com/sub/http://feeds.feedburner.com/StudioPragmatists" src="http://www.bloglines.com/images/sub_modern11.gif">Zapisz w Bloglines</feedburner:feedFlare><feedburner:feedFlare href="http://www.netvibes.com/subscribe.php?url=http%3A%2F%2Ffeeds.feedburner.com%2FStudioPragmatists" src="http://www.netvibes.com/img/add2netvibes.gif">Dodaj do netvibes</feedburner:feedFlare><feedburner:feedFlare href="http://fusion.google.com/add?feedurl=http%3A%2F%2Ffeeds.feedburner.com%2FStudioPragmatists" src="http://buttons.googlesyndication.com/fusion/add.gif">Dodaj do Google</feedburner:feedFlare><feedburner:feedFlare href="http://www.pageflakes.com/subscribe.aspx?url=http%3A%2F%2Ffeeds.feedburner.com%2FStudioPragmatists" src="http://www.pageflakes.com/ImageFile.ashx?instanceId=Static_4&amp;fileName=ATP_blu_91x17.gif">Subscribe with Pageflakes</feedburner:feedFlare><feedburner:feedFlare href="http://www.plusmo.com/add?url=http%3A%2F%2Ffeeds.feedburner.com%2FStudioPragmatists" src="http://plusmo.com/res/graphics/fbplusmo.gif">Subscribe with Plusmo</feedburner:feedFlare><feedburner:feedFlare href="http://www.thefreedictionary.com/_/hp/AddRSS.aspx?http%3A%2F%2Ffeeds.feedburner.com%2FStudioPragmatists" src="http://img.tfd.com/hp/addToTheFreeDictionary.gif">Subscribe with The Free Dictionary</feedburner:feedFlare><feedburner:feedFlare href="http://www.bitty.com/manual/?contenttype=rssfeed&amp;contentvalue=http%3A%2F%2Ffeeds.feedburner.com%2FStudioPragmatists" src="http://www.bitty.com/img/bittychicklet_91x17.gif">Subscribe with Bitty Browser</feedburner:feedFlare><feedburner:feedFlare href="http://www.live.com/?add=http%3A%2F%2Ffeeds.feedburner.com%2FStudioPragmatists" src="http://tkfiles.storage.msn.com/x1piYkpqHC_35nIp1gLE68-wvzLZO8iXl_JMledmJQXP-XTBOLfmQv4zhj4MhcWEJh_GtoBIiAl1Mjh-ndp9k47If7hTaFno0mxW9_i3p_5qQw">Subscribe with Live.com</feedburner:feedFlare><feedburner:feedFlare href="http://mix.excite.eu/add?feedurl=http%3A%2F%2Ffeeds.feedburner.com%2FStudioPragmatists" src="http://image.excite.co.uk/mix/addtomix.gif">Subscribe with Excite MIX</feedburner:feedFlare><feedburner:feedFlare href="http://www.webwag.com/wwgthis.php?url=http%3A%2F%2Ffeeds.feedburner.com%2FStudioPragmatists" src="http://www.webwag.com/images/wwgthis.gif">Subscribe with Webwag</feedburner:feedFlare><feedburner:feedFlare href="http://www.podcastready.com/oneclick_bookmark.php?url=http%3A%2F%2Ffeeds.feedburner.com%2FStudioPragmatists" src="http://www.podcastready.com/images/podcastready_button.gif">Subscribe with Podcast Ready</feedburner:feedFlare><feedburner:feedFlare href="http://www.wikio.com/subscribe?url=http%3A%2F%2Ffeeds.feedburner.com%2FStudioPragmatists" src="http://www.wikio.com/shared/img/add2wikio.gif">Subscribe with Wikio</feedburner:feedFlare><feedburner:feedFlare href="http://www.dailyrotation.com/index.php?feed=http%3A%2F%2Ffeeds.feedburner.com%2FStudioPragmatists" src="http://www.dailyrotation.com/rss-dr2.gif">Subscribe with Daily Rotation</feedburner:feedFlare><entry gd:etag="W/&quot;DUYASHw4fCp7ImA9WxFUFEk.&quot;"><id>tag:blogger.com,1999:blog-5009385396224025678.post-3157285028189300619</id><published>2010-06-24T20:46:00.001+02:00</published><updated>2010-06-25T08:25:49.234+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-06-25T08:25:49.234+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="konferencje" /><title>NYAC i Javarsovia</title><content type="html">W zeszły weekend odbyła się NYAC - nowa konferencja organizowana przez Grześka Dudę. Miałem przyjemność być zaproszonym do prowadzenia tam paru prelekcji, więc chciałem się z Wami podzielić moimi odczuciami.&lt;br /&gt;
&lt;br /&gt;
Zacznijmy od kwestii merytorycznych.&amp;nbsp;Agenda konferencji była zdecydowanie ciekawa. Mniej technologiczna niż większość konferencji, za to skupiona na jakości oprogramowania, technikach programistycznych, projektowaniu oprogramowania itp. Idea Grześka była taka, żeby prezentacje były mniej wykładowe, a bardziej w charakterze dyskusji. Myślę, że to fajna formuła, zachęcająca ludzi do dzielenia się doświadczeniami i wiedzą. I wg mnie ta formuła się sprawdziła. Ludzie stali na korytarzach w kółeczkach i rozmawiali. Również w czasie wykładów zawiązywały się dyskusje i wymiana doświadczeń.&lt;br /&gt;
&lt;br /&gt;
Organizacyjnie - myślę, że mogłoby być trochę lepiej (wizyta w tym hotelu to jak powrót do czasów 'Misia'), ale to pewnie związane ze sposobem finansowania konferencji.&lt;br /&gt;
&lt;br /&gt;
&lt;i&gt;Update: nie napisałem, że część konferencyjna była naprawdę ok. No może poza projektorem w głównej sali ;-)&lt;/i&gt;&lt;br /&gt;
&lt;br /&gt;
Ja na NYAC'u opowiadałem o:&lt;br /&gt;
- architekturze w zwinnych projektach&lt;br /&gt;
- wdrażaniu zwinnych praktyk w zespołach programistycznych&lt;br /&gt;
- razem z Bartkiem Bańkowskim kodowaliśmy (TDD) na żywo&lt;br /&gt;
- wraz z Bartkiem Bańkowskim i Szczepanem Faberem pokazywaliśmy testy akceptacyjne w różnych podejściach i technologiach - ja oczywiście pokazywałem &lt;a href="http://tumbler-glass.googlecode.com/"&gt;Tumbler&lt;/a&gt;'a&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
A już pojutrze &lt;a href="http://javarsovia.pl/"&gt;Javarsovia&lt;/a&gt;, na której opowiem o tym jak pisać testy, żeby były czytelne i wiecznie użyteczne. Zapraszam!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5009385396224025678-3157285028189300619?l=studiopragmatists.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/StudioPragmatists/~4/42u3Y4NXJo4" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://studiopragmatists.blogspot.com/feeds/3157285028189300619/comments/default" title="Komentarze do posta" /><link rel="replies" type="text/html" href="http://studiopragmatists.blogspot.com/2010/06/nyac-i-javarsovia.html#comment-form" title="Komentarze (2)" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5009385396224025678/posts/default/3157285028189300619?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5009385396224025678/posts/default/3157285028189300619?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/StudioPragmatists/~3/42u3Y4NXJo4/nyac-i-javarsovia.html" title="NYAC i Javarsovia" /><author><name>Paweł Lipiński</name><uri>http://www.blogger.com/profile/02772675014904905654</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="02130276274143705547" /></author><thr:total>2</thr:total><feedburner:origLink>http://studiopragmatists.blogspot.com/2010/06/nyac-i-javarsovia.html</feedburner:origLink></entry><entry gd:etag="W/&quot;A0ENRnw5fyp7ImA9WxFXFkU.&quot;"><id>tag:blogger.com,1999:blog-5009385396224025678.post-6744006837046461392</id><published>2010-05-24T08:36:00.004+02:00</published><updated>2010-05-24T10:28:17.227+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-05-24T10:28:17.227+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="tdd" /><title>Szkolenie TDD - retrospekcja</title><content type="html">&lt;div&gt;Już trochę czasu minęło od pierwszego otwartego &lt;a href="http://pragmatists.pl/pl/szkolenia/tdd.php"&gt;szkolenia Test-Driven Development&lt;/a&gt;, które miałem przyjemność prowadzić. Było to dla mnie na tyle ważne doświadczenie, że nadal odczuwam potrzebę podsumowania go. Zatem do dzieła...&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Uczenie programistów techniki TDD dało mi mnóstwo satysfakcji. Świetne było obserwowanie postępów uczestników. Widziałem też, że bardzo podobało się im podejście do programowania, w którym wiele "robi za nas" eclipse - chodzi o generowanie kodu i refaktoryzacje. TDD znakomicie promuje wykorzystanie możliwości IDE - w końcu chcemy ułatwić sobie życie i jak najwięcej robić automatycznie. No i okazuje się, że zaczynanie od testów sprawia, że &lt;b&gt;powstaje&lt;/b&gt; więcej kodu, ale wcale nie tak dużo więcej go &lt;b&gt;piszemy&lt;/b&gt;.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Podczas szkolenia starałem się nie zanudzać wykładami, prowadziłem natomiast dużo warsztatów. Podobało się to uczestnikom i myślę, że to właśnie praktyczne doświadczenie TDD jest najcenniejsze w tego typu szkoleniach.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Okazało się, że znakomicie zadziałało programowanie w parach. Wszyscy chętnie pracowali w ten sposób (co prawda nie jednocześnie, gdyż liczba osób była nieparzysta :) Praktyka programowania w parach doskonale uzupełnia się z TDD - cykl Red-Green-Refactor daje naturalne momenty zmiany &lt;i&gt;drivera&lt;/i&gt;. Dodatkowo świetny jest aspekt uczenia się od siebie nawzajem oraz doświadczenia różnych stylów programowania. Było na przykład tak, że jedna para wypracowywała pewien sposób pisania testów, który podczas następnego ćwiczenia "migrował" dalej (przed każdym warsztatem następowała zmiana w parach).&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Co do prowadzenia wykładów - doświadczyłem ogromnego znaczenia pilota do prezentacji. Pierwszego dnia zapomniałem go wziąć. Aby móc zmieniać slajdy, musiałem mówić stojąc blisko laptopa. Natomiast użycie tego urządzenia podczas następnych dni pozwoliło mi opowiadać dużo swobodniej - chodzić po sali, częściej utrzymywać kontakt wzrokowy ze słuchaczami. Zupełnie inna jakość.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Z rzeczy do poprawienia to na pewno warto uzupełnić prezentacje większą ilością przykładów - kodu i może trochę UMLa przy tematach z projektowania. Jeden obraz wart tysiąca słów, a kilka slajdów z kodem z pewnością równoważy wiele minut opowiadania "na sucho". Myślę tu głównie o refaktoryzacjach, zwłaszcza tych dotyczących kodu odziedziczonego. Ewentualnie zamiast kodu w prezentacjach niezłe mogłoby być też kodowanie na żywo.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;No i jakoś nie wyszło mi &lt;a href="http://iljapreuss.blogspot.com/2010/03/aha-experience-exercise.html"&gt;Aha Experience Exercise&lt;/a&gt;. Owszem, kilka "Aha!" pojawiło się na początku, gdy wprowadzałem to ćwiczenie, ale w trakcie już nie dochodziły kolejne. &lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Z niecierpliwością czekam na kolejną okazję, kiedy będę mógł wykorzystać doświadczenie z tego szkolenia i poprawić swój warsztat trenera i programisty. Tak, tak, programisty też - podczas wspólnego kodowania ja też uczę się od uczestników. Dziękuję!&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5009385396224025678-6744006837046461392?l=studiopragmatists.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/StudioPragmatists/~4/n4qpTSV5H7k" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://studiopragmatists.blogspot.com/feeds/6744006837046461392/comments/default" title="Komentarze do posta" /><link rel="replies" type="text/html" href="http://studiopragmatists.blogspot.com/2010/05/szkolenie-tdd-retrospekcja.html#comment-form" title="Komentarze (0)" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5009385396224025678/posts/default/6744006837046461392?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5009385396224025678/posts/default/6744006837046461392?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/StudioPragmatists/~3/n4qpTSV5H7k/szkolenie-tdd-retrospekcja.html" title="Szkolenie TDD - retrospekcja" /><author><name>Krzysztof Jelski</name><uri>http://www.blogger.com/profile/13328086479988072905</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="00293983052099926332" /></author><thr:total>0</thr:total><feedburner:origLink>http://studiopragmatists.blogspot.com/2010/05/szkolenie-tdd-retrospekcja.html</feedburner:origLink></entry><entry gd:etag="W/&quot;C0UNQH8-fip7ImA9WxFXEkQ.&quot;"><id>tag:blogger.com,1999:blog-5009385396224025678.post-3981188820340261638</id><published>2010-05-19T19:48:00.000+02:00</published><updated>2010-05-19T19:48:11.156+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-05-19T19:48:11.156+02:00</app:edited><title>Javarsovia 2010</title><content type="html">Wczoraj kapituła Javarsovii wysłała mi email, że moja prezentacja została zaakceptowana. Tak więc z przyjemnością ujawniam o czym będę mówił w tym roku:&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Temat:&lt;/b&gt;&amp;nbsp;"Clean Tests" by Uncle Paul, czyli jak pisać testy, żeby dobrze Ci służyły.&lt;br /&gt;
&lt;b&gt;Abstrakt:&lt;/b&gt;&amp;nbsp;Niewielu programistów ma dobry zestaw testów dla swoich aplikacji. Jeszcze mniej ma takie, które są zarówno dobrą “siatką bezpieczeństwa” przy realizacji jakichkolwiek zmian, ale również mogą służyć za zawsze aktualną dokumentację systemu. W czasie prezentacji pokażę jak pisać i refaktoryzować testy by się je łatwo czytało, nietrudno utrzymywało, oraz by dobrze dokumentowały aktualny stan systemu.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5009385396224025678-3981188820340261638?l=studiopragmatists.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/StudioPragmatists/~4/g8-PO-ob3Hs" height="1" width="1"/&gt;</content><link rel="related" href="http://javarsovia.pl/" title="Javarsovia 2010" /><link rel="replies" type="application/atom+xml" href="http://studiopragmatists.blogspot.com/feeds/3981188820340261638/comments/default" title="Komentarze do posta" /><link rel="replies" type="text/html" href="http://studiopragmatists.blogspot.com/2010/05/javarsovia-2010.html#comment-form" title="Komentarze (3)" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5009385396224025678/posts/default/3981188820340261638?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5009385396224025678/posts/default/3981188820340261638?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/StudioPragmatists/~3/g8-PO-ob3Hs/javarsovia-2010.html" title="Javarsovia 2010" /><author><name>Paweł Lipiński</name><uri>http://www.blogger.com/profile/02772675014904905654</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="02130276274143705547" /></author><thr:total>3</thr:total><feedburner:origLink>http://studiopragmatists.blogspot.com/2010/05/javarsovia-2010.html</feedburner:origLink></entry><entry gd:etag="W/&quot;A0AHQnY_eyp7ImA9WxFXEUU.&quot;"><id>tag:blogger.com,1999:blog-5009385396224025678.post-5867636482388809393</id><published>2010-05-17T23:07:00.001+02:00</published><updated>2010-05-18T15:35:33.843+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-05-18T15:35:33.843+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="tumbler" /><title>Tumbler 0.2.1</title><content type="html">Dzięki aktywności Bartka Zdanowskiego (a dokładniej jego niedoczytaniu dokumentacji ;-)) wypuściłem wersję 0.2.1, która jasno mówi jak coś jest nie tak. Czyli jak jest JUnit &amp;lt;4.6 albo ktoś używa @Test zamiast @Scenario. No more NPEs ;-)&lt;br /&gt;
&lt;br /&gt;
Do tego Tumbler 0.2.1 pojawi się w repo maven'a pewnie już jutro. Szczegóły będą na stronie Tumbler'a.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Aktualizacja:&amp;nbsp;&lt;/b&gt;&lt;br /&gt;
&lt;b&gt;Tumbler jest już w repo mavenowym.&lt;/b&gt;&lt;br /&gt;
&lt;b&gt;groupId: pl.pragmatists.tumbler&lt;/b&gt;&lt;br /&gt;
&lt;b&gt;artifactId: tumbler&lt;/b&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5009385396224025678-5867636482388809393?l=studiopragmatists.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/StudioPragmatists/~4/t-4SMoE2s3A" height="1" width="1"/&gt;</content><link rel="related" href="http://code.google.com/p/tumbler-glass/" title="Tumbler 0.2.1" /><link rel="replies" type="application/atom+xml" href="http://studiopragmatists.blogspot.com/feeds/5867636482388809393/comments/default" title="Komentarze do posta" /><link rel="replies" type="text/html" href="http://studiopragmatists.blogspot.com/2010/05/tumbler-021.html#comment-form" title="Komentarze (1)" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5009385396224025678/posts/default/5867636482388809393?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5009385396224025678/posts/default/5867636482388809393?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/StudioPragmatists/~3/t-4SMoE2s3A/tumbler-021.html" title="Tumbler 0.2.1" /><author><name>Paweł Lipiński</name><uri>http://www.blogger.com/profile/02772675014904905654</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="02130276274143705547" /></author><thr:total>1</thr:total><feedburner:origLink>http://studiopragmatists.blogspot.com/2010/05/tumbler-021.html</feedburner:origLink></entry><entry gd:etag="W/&quot;Dk4AQ30yeCp7ImA9WxFQGU0.&quot;"><id>tag:blogger.com,1999:blog-5009385396224025678.post-1829853420556470145</id><published>2010-05-14T23:01:00.008+02:00</published><updated>2010-05-15T08:29:02.390+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-05-15T08:29:02.390+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="bdd" /><category scheme="http://www.blogger.com/atom/ns#" term="tumbler" /><title>Tumbler 0.2.0</title><content type="html">Przez ostatnie dwa tygodnie znowu znalazłem trochę czasu i dorobiłem do Tumblera rzeczy, których brakowało. Dzięki temu wydałem właśnie &lt;a href="http://code.google.com/p/tumbler-glass/"&gt;wersję 0.2.0&lt;/a&gt;. W sumie powinienem chyba dobić do 1.0.0, bo w tej chwili ma on wszystkie podstawowe cechy takiego narzędzia, ale jakoś jeszcze nie jestem przekonany do takiego skoku w numerku.&lt;br /&gt;
Co się zmieniło?&lt;br /&gt;
&lt;ol&gt;&lt;li&gt;Zmieniłem moje wynalazki: Subject i Example na standardowe w BDD Story i Scenario. One może troszkę gorzej oddają o co chodzi, ale za to są spójne z zapisem Dana North'a używanym m.in. w JBehave, NBehave i jeszcze pewnie gdzieś indziej. A nuż ktoś będzie się chciał zmigrować, to będzie miał łatwiej. A i czytelnicy Dan'owych artykułów od razu zmapują sobie jego idee na Tumblerową rzeczywistość.&lt;/li&gt;
&lt;li&gt;Dodałem raporty w HTML. Jeśli to ma być narzędzie używane jakoś przez biznes, to musi być dla nich czytelne. Można używać własnych szablonów (freemarker) albo domyślnego. Zgodnie z sugestią Szczepana Fabera jest też "spis treści" dla &lt;i&gt;stories&lt;/i&gt;, wraz ze statystykami wykonania dla całego projektu.&lt;/li&gt;
&lt;li&gt;Za namową Lasse Koskeli usunąłem @CamelCase. Jeśli ktoś nie chce pełnych zdań w eclipsie, to niech sobie ustawi zmienną -DcamelCase=true. W ten sposób nie wpływa na resztę zespołu.&lt;/li&gt;
&lt;/ol&gt;W sumie więc nie ma tak dużo nowości, ale z drugiej strony myślę, że użyteczność narzędzia wzrosła znacznie.&lt;br /&gt;
&lt;br /&gt;
Bardzo chciałbym się dowiedzieć co myślicie o tym narzędziu. Co można by dodać, co powinno być inaczej.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Tumbler działa tylko z nowymi wersjami JUnit'a. Wersja domyślnie będąca w eclipsie, jest dość stara (i buggy) - 4.5. Do poprawnego działania Tumbler wymaga wersji 4.8. Użycie starszej wersji JUnit'a skutkuje następującym wyjątkiem:&lt;/b&gt;&lt;br /&gt;
&lt;blockquote&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;java.lang.NoSuchMethodError: org.junit.runner.Description.getMethodName()Ljava/lang/String;&lt;br /&gt;
at tumbler.internal.ScenarioListener.isItSingleScenario(ScenarioListener.java:97)&lt;br /&gt;
at tumbler.internal.ScenarioListener.testStarted(ScenarioListener.java:92)&lt;br /&gt;
at org.junit.runner.notification.RunNotifier$3.notifyListener(RunNotifier.java:83)&lt;br /&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/blockquote&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5009385396224025678-1829853420556470145?l=studiopragmatists.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/StudioPragmatists/~4/794yXxJxVIo" height="1" width="1"/&gt;</content><link rel="related" href="http://code.google.com/p/tumbler-glass/" title="Tumbler 0.2.0" /><link rel="replies" type="application/atom+xml" href="http://studiopragmatists.blogspot.com/feeds/1829853420556470145/comments/default" title="Komentarze do posta" /><link rel="replies" type="text/html" href="http://studiopragmatists.blogspot.com/2010/05/tumbler-020_14.html#comment-form" title="Komentarze (2)" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5009385396224025678/posts/default/1829853420556470145?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5009385396224025678/posts/default/1829853420556470145?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/StudioPragmatists/~3/794yXxJxVIo/tumbler-020_14.html" title="Tumbler 0.2.0" /><author><name>Paweł Lipiński</name><uri>http://www.blogger.com/profile/02772675014904905654</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="02130276274143705547" /></author><thr:total>2</thr:total><feedburner:origLink>http://studiopragmatists.blogspot.com/2010/05/tumbler-020_14.html</feedburner:origLink></entry><entry gd:etag="W/&quot;A0YHSXw4fyp7ImA9WxFQGEw.&quot;"><id>tag:blogger.com,1999:blog-5009385396224025678.post-1239793833521897135</id><published>2010-05-13T23:57:00.000+02:00</published><updated>2010-05-14T08:38:58.237+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-05-14T08:38:58.237+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="bdd" /><category scheme="http://www.blogger.com/atom/ns#" term="tumbler" /><title>Behaviour-Driven Development</title><content type="html">Tak sobie ostatnio pomyślałem, że wyjechałem z tym &lt;a href="http://tumbler-glass.googlecode.com/"&gt;Tumblerem&lt;/a&gt; bez przybliżenia najpierw o co w nim chodzi. To znaczy wyjaśniłem (mam nadzieję) o co w NIM chodzi, ale nie pisałem nigdy o samej idei na której on bazuje, czyli BDD. Samo hasło pewnie większość z Was słyszała, ale ciekawy jestem jaki procent wie o co w tym chodzi (nic, i tak się nie dowiem).&lt;br /&gt;
&lt;br /&gt;
No więc BDD wywodzi się od&amp;nbsp;&lt;a href="http://dannorth.net/"&gt;Dana North'a&lt;/a&gt;&amp;nbsp;(kiedyś z Thoughtworks, teraz nie wiem), który doszedł do wniosku, że jest jakaś dziura między tym co zwykle programiści myślą o projekcie, który robią, a tym czego oczekuje biznes. Główny problem jest taki, że każda z tych grup mówi innym językiem - biznes zasuwa wyłącznie o pieniądzach, a programiści chcą mieć wszystko wyłożone w javie. No więc Dan wpadł na pomysł jak to połączyć.&lt;br /&gt;
Po pierwsze trzeba się uporać z językiem - nie ma sensu, żeby każdy nazywał rzeczy po swojemu, więc Dan doszedł do wniosku, że skoro &lt;i&gt;nas klient nas Paaaannnnnn&lt;/i&gt;&amp;nbsp;to wszędzie należy używać tej nomenklatury (tak, to to samo co &lt;i&gt;Ubiquitous Language&lt;/i&gt;&amp;nbsp;w DDD).&lt;br /&gt;
Po drugie biznes(meni) patrzy na system z punktu widzenia jego użycia, a programiści z punktu widzenia... frameworków? Języka? Czy czegoś podobnego. Mówiąc w skrócie biznes patrzy z zewnątrz do wewnątrz a programiści odwrotnie.&lt;br /&gt;
No więc Dan wpadł na pomysł, żeby zbliżyć te wizje do siebie przez &lt;i&gt;zmuszenie&lt;/i&gt;&amp;nbsp;programistów do tego, żeby myśleli po angielsku, a więc za pomocą rzeczowników i czasowników a nie instancji i metod. Konkretnie chodziło mu o to, żeby wyrażać testy jako przykłady &lt;i&gt;(coding by example)&lt;/i&gt;&amp;nbsp;wyrażane w języku naturalnym (angielskim, bo: primo - polska fleksja nie nadaje się do parsowania, secundo - Dan nie włada polszczyzną). Idealnie powinno być tak, że &lt;i&gt;nieprogramiści &lt;/i&gt;(tak, to jednak pewnie grubo ponad 99% ludzkości) są w stanie to przeczytać i zrozumieć. Tu wchodzimy na grząski grunt DSLi, którymi nie chcę się dziś zajmować, bo jest późno, a i tak już dużo napisałem.&lt;br /&gt;
Wracając do tematu, Dan wymyślił dość zwięzły i krótki zapis, który jest zrozumiały dla &lt;i&gt;innych&lt;/i&gt;&amp;nbsp;(no..., że nie tylko dla nas), a przy okazji jest parsowalny i da się przerobić łatwo na coś zrozumiałego dla maszyn. Zapis Dana jest następujący&amp;nbsp;(ukradłem ten tekst ze strony Dana, więc mu nie mówcie):&lt;br /&gt;
&lt;blockquote&gt;&lt;span class="Apple-style-span" style="color: #555555; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace; font-size: 11px; line-height: 14px;"&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="color: #555555; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace; font-size: 11px; line-height: 14px;"&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="color: #555555; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace; font-size: 11px; line-height: 14px;"&gt;&lt;div class="line alt1" style="background-attachment: initial !important; background-clip: initial !important; background-color: rgb(255, 255, 255) !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-width: 0px !important; bottom: auto !important; direction: ltr !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 11px !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.3em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; text-shadow: none !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;&lt;table style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-width: 0px !important; border-collapse: collapse !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-width: 0px !important; bottom: auto !important; direction: ltr !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 11px !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.3em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; text-shadow: none !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;&lt;tbody style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-width: 0px !important; bottom: auto !important; direction: ltr !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 11px !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.3em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; text-shadow: none !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;
&lt;tr style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-width: 0px !important; bottom: auto !important; direction: ltr !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 11px !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.3em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; text-shadow: none !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;&lt;td class="content" style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-color: initial !important; border-left-style: none !important; border-left-width: initial !important; border-right-width: 0px !important; border-style: initial !important; border-top-width: 0px !important; bottom: auto !important; color: rgb(0, 0, 0) !important; direction: ltr !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 11px !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.3em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; padding-bottom: 0px !important; padding-left: 0.5em !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; text-shadow: none !important; top: auto !important; vertical-align: top !important; width: auto !important;"&gt;&lt;code class="plain" style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-width: 0px !important; bottom: auto !important; color: rgb(0, 0, 0) !important; direction: ltr !important; display: inline !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 11px !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.3em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; text-shadow: none !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;Title (one line describing the story)&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;div class="line alt2" style="background-attachment: initial !important; background-clip: initial !important; background-color: rgb(248, 248, 248) !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-width: 0px !important; bottom: auto !important; direction: ltr !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 11px !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.3em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; text-shadow: none !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;&lt;table style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-width: 0px !important; border-collapse: collapse !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-width: 0px !important; bottom: auto !important; direction: ltr !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 11px !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.3em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; text-shadow: none !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;&lt;tbody style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-width: 0px !important; bottom: auto !important; direction: ltr !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 11px !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.3em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; text-shadow: none !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;
&lt;tr style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-width: 0px !important; bottom: auto !important; direction: ltr !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 11px !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.3em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; text-shadow: none !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;&lt;td class="content" style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-color: initial !important; border-left-style: none !important; border-left-width: initial !important; border-right-width: 0px !important; border-style: initial !important; border-top-width: 0px !important; bottom: auto !important; color: rgb(0, 0, 0) !important; direction: ltr !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 11px !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.3em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; padding-bottom: 0px !important; padding-left: 0.5em !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; text-shadow: none !important; top: auto !important; vertical-align: top !important; width: auto !important;"&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;div class="line alt1" style="background-attachment: initial !important; background-clip: initial !important; background-color: rgb(255, 255, 255) !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-width: 0px !important; bottom: auto !important; direction: ltr !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 11px !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.3em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; text-shadow: none !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;&lt;table style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-width: 0px !important; border-collapse: collapse !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-width: 0px !important; bottom: auto !important; direction: ltr !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 11px !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.3em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; text-shadow: none !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;&lt;tbody style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-width: 0px !important; bottom: auto !important; direction: ltr !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 11px !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.3em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; text-shadow: none !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;
&lt;tr style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-width: 0px !important; bottom: auto !important; direction: ltr !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 11px !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.3em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; text-shadow: none !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;&lt;td class="content" style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-color: initial !important; border-left-style: none !important; border-left-width: initial !important; border-right-width: 0px !important; border-style: initial !important; border-top-width: 0px !important; bottom: auto !important; color: rgb(0, 0, 0) !important; direction: ltr !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 11px !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.3em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; padding-bottom: 0px !important; padding-left: 0.5em !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; text-shadow: none !important; top: auto !important; vertical-align: top !important; width: auto !important;"&gt;&lt;code class="plain" style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-width: 0px !important; bottom: auto !important; color: rgb(0, 0, 0) !important; direction: ltr !important; display: inline !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 11px !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.3em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; text-shadow: none !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;Narrative:&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;div class="line alt2" style="background-attachment: initial !important; background-clip: initial !important; background-color: rgb(248, 248, 248) !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-width: 0px !important; bottom: auto !important; direction: ltr !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 11px !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.3em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; text-shadow: none !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;&lt;table style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-width: 0px !important; border-collapse: collapse !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-width: 0px !important; bottom: auto !important; direction: ltr !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 11px !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.3em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; text-shadow: none !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;&lt;tbody style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-width: 0px !important; bottom: auto !important; direction: ltr !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 11px !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.3em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; text-shadow: none !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;
&lt;tr style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-width: 0px !important; bottom: auto !important; direction: ltr !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 11px !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.3em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; text-shadow: none !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;&lt;td class="content" style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-color: initial !important; border-left-style: none !important; border-left-width: initial !important; border-right-width: 0px !important; border-style: initial !important; border-top-width: 0px !important; bottom: auto !important; color: rgb(0, 0, 0) !important; direction: ltr !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 11px !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.3em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; padding-bottom: 0px !important; padding-left: 0.5em !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; text-shadow: none !important; top: auto !important; vertical-align: top !important; width: auto !important;"&gt;&lt;code class="plain" style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-width: 0px !important; bottom: auto !important; color: rgb(0, 0, 0) !important; direction: ltr !important; display: inline !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 11px !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.3em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; text-shadow: none !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;As a [role]&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;div class="line alt1" style="background-attachment: initial !important; background-clip: initial !important; background-color: rgb(255, 255, 255) !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-width: 0px !important; bottom: auto !important; direction: ltr !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 11px !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.3em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; text-shadow: none !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;&lt;table style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-width: 0px !important; border-collapse: collapse !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-width: 0px !important; bottom: auto !important; direction: ltr !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 11px !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.3em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; text-shadow: none !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;&lt;tbody style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-width: 0px !important; bottom: auto !important; direction: ltr !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 11px !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.3em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; text-shadow: none !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;
&lt;tr style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-width: 0px !important; bottom: auto !important; direction: ltr !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 11px !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.3em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; text-shadow: none !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;&lt;td class="content" style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-color: initial !important; border-left-style: none !important; border-left-width: initial !important; border-right-width: 0px !important; border-style: initial !important; border-top-width: 0px !important; bottom: auto !important; color: rgb(0, 0, 0) !important; direction: ltr !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 11px !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.3em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; padding-bottom: 0px !important; padding-left: 0.5em !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; text-shadow: none !important; top: auto !important; vertical-align: top !important; width: auto !important;"&gt;&lt;code class="plain" style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-width: 0px !important; bottom: auto !important; color: rgb(0, 0, 0) !important; direction: ltr !important; display: inline !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 11px !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.3em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; text-shadow: none !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;I want [feature]&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;div class="line alt2" style="background-attachment: initial !important; background-clip: initial !important; background-color: rgb(248, 248, 248) !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-width: 0px !important; bottom: auto !important; direction: ltr !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 11px !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.3em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; text-shadow: none !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;&lt;table style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-width: 0px !important; border-collapse: collapse !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-width: 0px !important; bottom: auto !important; direction: ltr !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 11px !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.3em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; text-shadow: none !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;&lt;tbody style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-width: 0px !important; bottom: auto !important; direction: ltr !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 11px !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.3em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; text-shadow: none !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;
&lt;tr style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-width: 0px !important; bottom: auto !important; direction: ltr !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 11px !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.3em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; text-shadow: none !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;&lt;td class="content" style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-color: initial !important; border-left-style: none !important; border-left-width: initial !important; border-right-width: 0px !important; border-style: initial !important; border-top-width: 0px !important; bottom: auto !important; color: rgb(0, 0, 0) !important; direction: ltr !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 11px !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.3em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; padding-bottom: 0px !important; padding-left: 0.5em !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; text-shadow: none !important; top: auto !important; vertical-align: top !important; width: auto !important;"&gt;&lt;code class="plain" style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-width: 0px !important; bottom: auto !important; color: rgb(0, 0, 0) !important; direction: ltr !important; display: inline !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 11px !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.3em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; text-shadow: none !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;So that [benefit]&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;div class="line alt1" style="background-attachment: initial !important; background-clip: initial !important; background-color: rgb(255, 255, 255) !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-width: 0px !important; bottom: auto !important; direction: ltr !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 11px !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.3em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; text-shadow: none !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;&lt;table style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-width: 0px !important; border-collapse: collapse !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-width: 0px !important; bottom: auto !important; direction: ltr !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 11px !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.3em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; text-shadow: none !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;&lt;tbody style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-width: 0px !important; bottom: auto !important; direction: ltr !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 11px !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.3em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; text-shadow: none !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;
&lt;tr style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-width: 0px !important; bottom: auto !important; direction: ltr !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 11px !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.3em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; text-shadow: none !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;&lt;td class="content" style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-color: initial !important; border-left-style: none !important; border-left-width: initial !important; border-right-width: 0px !important; border-style: initial !important; border-top-width: 0px !important; bottom: auto !important; color: rgb(0, 0, 0) !important; direction: ltr !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 11px !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.3em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; padding-bottom: 0px !important; padding-left: 0.5em !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; text-shadow: none !important; top: auto !important; vertical-align: top !important; width: auto !important;"&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;div class="line alt2" style="background-attachment: initial !important; background-clip: initial !important; background-color: rgb(248, 248, 248) !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-width: 0px !important; bottom: auto !important; direction: ltr !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 11px !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.3em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; text-shadow: none !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;&lt;table style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-width: 0px !important; border-collapse: collapse !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-width: 0px !important; bottom: auto !important; direction: ltr !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 11px !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.3em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; text-shadow: none !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;&lt;tbody style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-width: 0px !important; bottom: auto !important; direction: ltr !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 11px !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.3em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; text-shadow: none !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;
&lt;tr style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-width: 0px !important; bottom: auto !important; direction: ltr !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 11px !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.3em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; text-shadow: none !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;&lt;td class="content" style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-color: initial !important; border-left-style: none !important; border-left-width: initial !important; border-right-width: 0px !important; border-style: initial !important; border-top-width: 0px !important; bottom: auto !important; color: rgb(0, 0, 0) !important; direction: ltr !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 11px !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.3em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; padding-bottom: 0px !important; padding-left: 0.5em !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; text-shadow: none !important; top: auto !important; vertical-align: top !important; width: auto !important;"&gt;&lt;code class="plain" style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-width: 0px !important; bottom: auto !important; color: rgb(0, 0, 0) !important; direction: ltr !important; display: inline !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 11px !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.3em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; text-shadow: none !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;Acceptance Criteria: (presented as Scenarios)&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;div class="line alt1" style="background-attachment: initial !important; background-clip: initial !important; background-color: rgb(255, 255, 255) !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-width: 0px !important; bottom: auto !important; direction: ltr !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 11px !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.3em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; text-shadow: none !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;&lt;table style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-width: 0px !important; border-collapse: collapse !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-width: 0px !important; bottom: auto !important; direction: ltr !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 11px !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.3em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; text-shadow: none !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;&lt;tbody style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-width: 0px !important; bottom: auto !important; direction: ltr !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 11px !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.3em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; text-shadow: none !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;
&lt;tr style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-width: 0px !important; bottom: auto !important; direction: ltr !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 11px !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.3em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; text-shadow: none !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;&lt;td class="content" style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-color: initial !important; border-left-style: none !important; border-left-width: initial !important; border-right-width: 0px !important; border-style: initial !important; border-top-width: 0px !important; bottom: auto !important; color: rgb(0, 0, 0) !important; direction: ltr !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 11px !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.3em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; padding-bottom: 0px !important; padding-left: 0.5em !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; text-shadow: none !important; top: auto !important; vertical-align: top !important; width: auto !important;"&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;div class="line alt2" style="background-attachment: initial !important; background-clip: initial !important; background-color: rgb(248, 248, 248) !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-width: 0px !important; bottom: auto !important; direction: ltr !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 11px !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.3em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; text-shadow: none !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;&lt;table style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-width: 0px !important; border-collapse: collapse !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-width: 0px !important; bottom: auto !important; direction: ltr !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 11px !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.3em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; text-shadow: none !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;&lt;tbody style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-width: 0px !important; bottom: auto !important; direction: ltr !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 11px !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.3em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; text-shadow: none !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;
&lt;tr style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-width: 0px !important; bottom: auto !important; direction: ltr !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 11px !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.3em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; text-shadow: none !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;&lt;td class="content" style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-color: initial !important; border-left-style: none !important; border-left-width: initial !important; border-right-width: 0px !important; border-style: initial !important; border-top-width: 0px !important; bottom: auto !important; color: rgb(0, 0, 0) !important; direction: ltr !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 11px !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.3em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; padding-bottom: 0px !important; padding-left: 0.5em !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; text-shadow: none !important; top: auto !important; vertical-align: top !important; width: auto !important;"&gt;&lt;code class="plain" style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-width: 0px !important; bottom: auto !important; color: rgb(0, 0, 0) !important; direction: ltr !important; display: inline !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 11px !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.3em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; text-shadow: none !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;Scenario 1: Title&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;div class="line alt1" style="background-attachment: initial !important; background-clip: initial !important; background-color: rgb(255, 255, 255) !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-width: 0px !important; bottom: auto !important; direction: ltr !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 11px !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.3em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; text-shadow: none !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;&lt;table style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-width: 0px !important; border-collapse: collapse !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-width: 0px !important; bottom: auto !important; direction: ltr !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 11px !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.3em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; text-shadow: none !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;&lt;tbody style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-width: 0px !important; bottom: auto !important; direction: ltr !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 11px !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.3em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; text-shadow: none !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;
&lt;tr style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-width: 0px !important; bottom: auto !important; direction: ltr !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 11px !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.3em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; text-shadow: none !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;&lt;td class="content" style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-color: initial !important; border-left-style: none !important; border-left-width: initial !important; border-right-width: 0px !important; border-style: initial !important; border-top-width: 0px !important; bottom: auto !important; color: rgb(0, 0, 0) !important; direction: ltr !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 11px !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.3em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; padding-bottom: 0px !important; padding-left: 0.5em !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; text-shadow: none !important; top: auto !important; vertical-align: top !important; width: auto !important;"&gt;&lt;code class="plain" style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-width: 0px !important; bottom: auto !important; color: rgb(0, 0, 0) !important; direction: ltr !important; display: inline !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 11px !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.3em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; text-shadow: none !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;Given [context]&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;div class="line alt2" style="background-attachment: initial !important; background-clip: initial !important; background-color: rgb(248, 248, 248) !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-width: 0px !important; bottom: auto !important; direction: ltr !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 11px !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.3em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; text-shadow: none !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;&lt;table style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-width: 0px !important; border-collapse: collapse !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-width: 0px !important; bottom: auto !important; direction: ltr !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 11px !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.3em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; text-shadow: none !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;&lt;tbody style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-width: 0px !important; bottom: auto !important; direction: ltr !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 11px !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.3em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; text-shadow: none !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;
&lt;tr style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-width: 0px !important; bottom: auto !important; direction: ltr !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 11px !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.3em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; text-shadow: none !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;&lt;td class="content" style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-color: initial !important; border-left-style: none !important; border-left-width: initial !important; border-right-width: 0px !important; border-style: initial !important; border-top-width: 0px !important; bottom: auto !important; color: rgb(0, 0, 0) !important; direction: ltr !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 11px !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.3em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; padding-bottom: 0px !important; padding-left: 0.5em !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; text-shadow: none !important; top: auto !important; vertical-align: top !important; width: auto !important;"&gt;&lt;code class="spaces" style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-width: 0px !important; bottom: auto !important; direction: ltr !important; display: inline !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 11px !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.3em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; text-shadow: none !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;&amp;nbsp;&amp;nbsp;&lt;/code&gt;&lt;code class="plain" style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-width: 0px !important; bottom: auto !important; color: rgb(0, 0, 0) !important; direction: ltr !important; display: inline !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 11px !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.3em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; text-shadow: none !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;And [some more context]...&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;div class="line alt1" style="background-attachment: initial !important; background-clip: initial !important; background-color: rgb(255, 255, 255) !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-width: 0px !important; bottom: auto !important; direction: ltr !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 11px !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.3em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; text-shadow: none !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;&lt;table style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-width: 0px !important; border-collapse: collapse !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-width: 0px !important; bottom: auto !important; direction: ltr !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 11px !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.3em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; text-shadow: none !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;&lt;tbody style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-width: 0px !important; bottom: auto !important; direction: ltr !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 11px !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.3em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; text-shadow: none !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;
&lt;tr style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-width: 0px !important; bottom: auto !important; direction: ltr !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 11px !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.3em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; text-shadow: none !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;&lt;td class="content" style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-color: initial !important; border-left-style: none !important; border-left-width: initial !important; border-right-width: 0px !important; border-style: initial !important; border-top-width: 0px !important; bottom: auto !important; color: rgb(0, 0, 0) !important; direction: ltr !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 11px !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.3em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; padding-bottom: 0px !important; padding-left: 0.5em !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; text-shadow: none !important; top: auto !important; vertical-align: top !important; width: auto !important;"&gt;&lt;code class="plain" style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-width: 0px !important; bottom: auto !important; color: rgb(0, 0, 0) !important; direction: ltr !important; display: inline !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 11px !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.3em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; text-shadow: none !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;When&amp;nbsp; [event]&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;div class="line alt2" style="background-attachment: initial !important; background-clip: initial !important; background-color: rgb(248, 248, 248) !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-width: 0px !important; bottom: auto !important; direction: ltr !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 11px !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.3em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; text-shadow: none !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;&lt;table style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-width: 0px !important; border-collapse: collapse !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-width: 0px !important; bottom: auto !important; direction: ltr !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 11px !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.3em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; text-shadow: none !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;&lt;tbody style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-width: 0px !important; bottom: auto !important; direction: ltr !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 11px !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.3em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; text-shadow: none !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;
&lt;tr style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-width: 0px !important; bottom: auto !important; direction: ltr !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 11px !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.3em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; text-shadow: none !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;&lt;td class="content" style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-color: initial !important; border-left-style: none !important; border-left-width: initial !important; border-right-width: 0px !important; border-style: initial !important; border-top-width: 0px !important; bottom: auto !important; color: rgb(0, 0, 0) !important; direction: ltr !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 11px !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.3em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; padding-bottom: 0px !important; padding-left: 0.5em !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; text-shadow: none !important; top: auto !important; vertical-align: top !important; width: auto !important;"&gt;&lt;code class="plain" style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-width: 0px !important; bottom: auto !important; color: rgb(0, 0, 0) !important; direction: ltr !important; display: inline !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 11px !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.3em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; text-shadow: none !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;Then&amp;nbsp; [outcome]&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;div class="line alt1" style="background-attachment: initial !important; background-clip: initial !important; background-color: rgb(255, 255, 255) !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-width: 0px !important; bottom: auto !important; direction: ltr !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 11px !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.3em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; text-shadow: none !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;&lt;table style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-width: 0px !important; border-collapse: collapse !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-width: 0px !important; bottom: auto !important; direction: ltr !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 11px !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.3em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; text-shadow: none !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;&lt;tbody style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-width: 0px !important; bottom: auto !important; direction: ltr !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 11px !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.3em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; text-shadow: none !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;
&lt;tr style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-width: 0px !important; bottom: auto !important; direction: ltr !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 11px !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.3em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; text-shadow: none !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;&lt;td class="content" style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-color: initial !important; border-left-style: none !important; border-left-width: initial !important; border-right-width: 0px !important; border-style: initial !important; border-top-width: 0px !important; bottom: auto !important; color: rgb(0, 0, 0) !important; direction: ltr !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 11px !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.3em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; padding-bottom: 0px !important; padding-left: 0.5em !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; text-shadow: none !important; top: auto !important; vertical-align: top !important; width: auto !important;"&gt;&lt;code class="spaces" style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-width: 0px !important; bottom: auto !important; direction: ltr !important; display: inline !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 11px !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.3em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; text-shadow: none !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;&amp;nbsp;&amp;nbsp;&lt;/code&gt;&lt;code class="plain" style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-width: 0px !important; bottom: auto !important; color: rgb(0, 0, 0) !important; direction: ltr !important; display: inline !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 11px !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.3em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; text-shadow: none !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;And [another outcome]...&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;div class="line alt2" style="background-attachment: initial !important; background-clip: initial !important; background-color: rgb(248, 248, 248) !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-width: 0px !important; bottom: auto !important; direction: ltr !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 11px !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.3em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; text-shadow: none !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;&lt;table style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-width: 0px !important; border-collapse: collapse !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-width: 0px !important; bottom: auto !important; direction: ltr !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 11px !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.3em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; text-shadow: none !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;&lt;tbody style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-width: 0px !important; bottom: auto !important; direction: ltr !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 11px !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.3em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; text-shadow: none !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;
&lt;tr style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-width: 0px !important; bottom: auto !important; direction: ltr !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 11px !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.3em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; text-shadow: none !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;&lt;td class="content" style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-color: initial !important; border-left-style: none !important; border-left-width: initial !important; border-right-width: 0px !important; border-style: initial !important; border-top-width: 0px !important; bottom: auto !important; color: rgb(0, 0, 0) !important; direction: ltr !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 11px !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.3em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; padding-bottom: 0px !important; padding-left: 0.5em !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; text-shadow: none !important; top: auto !important; vertical-align: top !important; width: auto !important;"&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;div class="line alt1" style="background-attachment: initial !important; background-clip: initial !important; background-color: rgb(255, 255, 255) !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-width: 0px !important; bottom: auto !important; direction: ltr !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 11px !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.3em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; text-shadow: none !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;&lt;table style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-width: 0px !important; border-collapse: collapse !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-width: 0px !important; bottom: auto !important; direction: ltr !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 11px !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.3em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; text-shadow: none !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;&lt;tbody style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-width: 0px !important; bottom: auto !important; direction: ltr !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 11px !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.3em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; text-shadow: none !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;
&lt;tr style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-width: 0px !important; bottom: auto !important; direction: ltr !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 11px !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.3em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; text-shadow: none !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;&lt;td class="content" style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-color: initial !important; border-left-style: none !important; border-left-width: initial !important; border-right-width: 0px !important; border-style: initial !important; border-top-width: 0px !important; bottom: auto !important; color: rgb(0, 0, 0) !important; direction: ltr !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 11px !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.3em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; padding-bottom: 0px !important; padding-left: 0.5em !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; text-shadow: none !important; top: auto !important; vertical-align: top !important; width: auto !important;"&gt;&lt;code class="plain" style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-width: 0px !important; bottom: auto !important; color: rgb(0, 0, 0) !important; direction: ltr !important; display: inline !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 11px !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.3em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; text-shadow: none !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;Scenario 2: ...&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;/span&gt;&lt;/blockquote&gt;&lt;i&gt;Story&lt;/i&gt; to coś a'la User Story w zwinnych metodykach, czyli jakaś funcjonalność. &lt;i&gt;Narrative&lt;/i&gt; to znów znany ze zwinnych metodyk zwrocik (oj, zdrobnienie - wyszło, żem Warszawiak) "Jako KTOŚTAM chciałbym COŚTAM żeby COŚ SIĘ STAŁO". Do tego dochodzi &lt;i&gt;Scenario&lt;/i&gt;, czyli konkretny przykład stosowania funkcjonalności, w formie znanej z wykładów Szczepana Fabera:&lt;br /&gt;
&lt;blockquote&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;Given&amp;nbsp;&amp;nbsp;...&lt;br /&gt;
When&amp;nbsp;...&lt;br /&gt;
Then&amp;nbsp;...&lt;/span&gt;&lt;/blockquote&gt;Czyli prosty zapis tego co dane jest na wejściu, operacji, i jej wyniku.&lt;br /&gt;
Takie zdania powinien być w stanie nam dostarczyć każdy analityk (no dobra, tak naprawdę, to biznes powinien siedzieć nad tym z programistą, w końcu &lt;i&gt;customer on-site&lt;/i&gt;...) A my z tych zdań budujemy szybko sobie testy, które prowadzą (młodzież powiedziałaby &lt;i&gt;drajwują&lt;/i&gt;, ale ja jestem stary)&amp;nbsp;zarówno zakres funkcjonalny tworzonej aplikacji, jak i w pewnym stopniu design - przynajmniej API.&lt;br /&gt;
&lt;br /&gt;
Podsumowując - chodzi o to, by mieć wspólny zapis dla przykładów, które prowadzą development, oraz by te przykłady były wybierane (przez biznes) na podstawie ich wartości dla systemu. Realizacja tego pomysłu powoduje, że cały development jest prowadzony de facto przez interfejsy użytkownika, bo to jest to, o czym biznes myśli i co jest sobie w stanie wyobrazić (i dobrze!)&lt;br /&gt;
&lt;br /&gt;
Do tego w BDD jest cała&amp;nbsp;podszewka lingwistyczno-psychologiczna (powinienem pojechać tu hipotezą &lt;a href="http://en.wikipedia.org/wiki/Linguistic_relativity"&gt;Sapir'a-Whorf'a&lt;/a&gt;&amp;nbsp;ale na to jest zdecydowanie za późno....), która mówi o tym, że programiści &lt;i&gt;lepiej&lt;/i&gt; myślą o kodzie jeśli rozumieją i wyrażają go po ludzku.&lt;br /&gt;
&lt;br /&gt;
No i na koniec wisienka na torcie: oczywiście przykłady to tak naprawdę testy. Same przykłady to już dużo, bo pozwalają myśleć o kodzie z punktu widzenia jego użycia. Ale skoro przykłady opracowuje się na początku - &lt;b&gt;przed&lt;/b&gt; pisaniem kodu, to chyba jest jasne, że nie wyrazić ich w postaci testów było by głupotą. No więc w ten sposób przykłady zachowań z BDD stykają się z testami akceptacyjnymi, prowadząc do A-TDD (Acceptance-Test Driven Development).&lt;br /&gt;
&lt;br /&gt;
No, to chyba tyle. To teraz jak to się ma do Tumbler'a?&lt;br /&gt;
Jak już wiemy o co chodzi, to Tumbler po prostu realizuje tę ideę i trochę ją rozszerza (cóż - jestem przekonany, że Dan nie wymyślił tego po ubarwionej zielskiem wycieczce po Camden Town, tylko faktycznie miał to na myśli). Nie tylko pozwala on przekształcać pełnotekstowe przykłady/scenariusze (o składni +/- takiej jak powyższa) na testy JUnit'owe, ale generuje również raporty (aktualnie txt oraz html) z tego co przechodzi, co nie, a co jeszcze nie zostało zaimplementowane. To już dużo. Jednak główną myślą, która mi przyświecała przy tworzeniu tego narzędzia było, że większość programistów nie ma planu co ma zrealizować, tylko pisze co im się akurat wydaje w danym momencie ważne. Tak powstają nie tylko potwory i spółka, ale również po prostu niekompletne implementacje. A gdyby tak zastanowić się chwilę nad każdą funkcjonalnością, zapisać ją w formie przykładów i dopiero potem zrealizować? Dam głowę, że tak napisany kod nie powodowałby u autora myśli "gdybym tylko mógł napisać to jeszcze raz!" - bo raz piszemy to najpierw&amp;nbsp;&lt;i&gt;w głowie&lt;/i&gt;&amp;nbsp;w przykładach, a potem przelewamy na papier - &amp;nbsp;tfu, klawiaturę - w dopracowanej formie.&lt;br /&gt;
&lt;br /&gt;
PS. Tumbler z troszkę zmienionym API oraz nowymi funkcjonalnościami wyjdzie w ten weekend, więc kto żyw ściągać w poniedziałek rano i stosować! ;-)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5009385396224025678-1239793833521897135?l=studiopragmatists.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/StudioPragmatists/~4/SjAa3vJfwWg" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://studiopragmatists.blogspot.com/feeds/1239793833521897135/comments/default" title="Komentarze do posta" /><link rel="replies" type="text/html" href="http://studiopragmatists.blogspot.com/2010/05/behaviour-driven-development.html#comment-form" title="Komentarze (0)" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5009385396224025678/posts/default/1239793833521897135?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5009385396224025678/posts/default/1239793833521897135?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/StudioPragmatists/~3/SjAa3vJfwWg/behaviour-driven-development.html" title="Behaviour-Driven Development" /><author><name>Paweł Lipiński</name><uri>http://www.blogger.com/profile/02772675014904905654</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="02130276274143705547" /></author><thr:total>0</thr:total><feedburner:origLink>http://studiopragmatists.blogspot.com/2010/05/behaviour-driven-development.html</feedburner:origLink></entry><entry gd:etag="W/&quot;D0QNR3k9eCp7ImA9WxFQFks.&quot;"><id>tag:blogger.com,1999:blog-5009385396224025678.post-5964764984227636703</id><published>2010-05-12T13:56:00.000+02:00</published><updated>2010-05-12T13:56:36.760+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-05-12T13:56:36.760+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="agile warsaw" /><title>Agile Warsaw</title><content type="html">Miło mi poinformować, że założona została grupa Agile Warsaw, która ma na celu stworzenie w Warszawie społeczności ludzi związanych ze zwinnym wytwarzaniem oprogramowania. Chcielibyśmy utworzyć przestrzeń z klimatem wzajemnego uczenia się, wymiany doświadczeń, wspólnego szukania nowych dróg do lepszego tworzenia oprogramowania, prowadzenia zespołów i zaspokajania potrzeb naszych klientów. Chcielibyśmy by to środowisko samo było zwinne, dlatego szczególną wagę przykładać będziemy do prostoty, &amp;nbsp;komunikacji, informacji zwrotnej, odwagi i szacunku. Jeśli więc starasz się realizować te wartości w swojej pracy, chciałbyś dowiedzieć się jak pracują inni i dzielić się swoimi zwinnymi doświadczeniami - jesteś więcej niż mile widziany.&lt;br /&gt;
Spotkania planowane są w wybrane poniedziałki, o godzinie 19stej w siedzibie firmy Aenima, ul Łucka 15 pok. 227. Planowana formuły spotkań to mini-wykład (15-30min) po którym następuje dyskusja i wymiana doświadczeń, oraz Open Space Technology. Spotkania kończą się retrospektywą / socjalizacją / networkingiem w okolicznym pubie.&lt;br /&gt;
&lt;br /&gt;
Najbliższe spotkanie odbędzie się 24tego maja, tematem przewodnim będzie przejście ze Scruma do Kanabana, a poprowadzi go Marek Kirejczyk.&lt;br /&gt;
&lt;br /&gt;
Agile Warsaw ma też swoje miejsce w internecie - jest to &lt;a href="http://groups.google.com/group/agile-warsaw"&gt;googlowa grupa agile-warsaw&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
Zapraszamy!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5009385396224025678-5964764984227636703?l=studiopragmatists.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/StudioPragmatists/~4/TxL2p7S_buw" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://studiopragmatists.blogspot.com/feeds/5964764984227636703/comments/default" title="Komentarze do posta" /><link rel="replies" type="text/html" href="http://studiopragmatists.blogspot.com/2010/05/agile-warsaw.html#comment-form" title="Komentarze (2)" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5009385396224025678/posts/default/5964764984227636703?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5009385396224025678/posts/default/5964764984227636703?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/StudioPragmatists/~3/TxL2p7S_buw/agile-warsaw.html" title="Agile Warsaw" /><author><name>Paweł Lipiński</name><uri>http://www.blogger.com/profile/02772675014904905654</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="02130276274143705547" /></author><thr:total>2</thr:total><feedburner:origLink>http://studiopragmatists.blogspot.com/2010/05/agile-warsaw.html</feedburner:origLink></entry><entry gd:etag="W/&quot;Ck4NQHY6fip7ImA9WxFQEU0.&quot;"><id>tag:blogger.com,1999:blog-5009385396224025678.post-3753299084369288232</id><published>2010-05-06T01:07:00.000+02:00</published><updated>2010-05-06T01:09:51.816+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-05-06T01:09:51.816+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="java" /><title>Płynne interfejsy</title><content type="html">Ten post powstał na wyraźną prośbę Jacka Laskowskiego i Bartka Zdanowskiego, którzy zastanawiali się (w komentarzach poprzedniego posta) nad kawałkiem przykładu z dokumentacji &lt;a href="http://tumbler-glass.googlecode.com/"&gt;Tumbler'a&lt;/a&gt;. Przykład wygląda tak:&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/_9g_SKM27_UE/S-Hh4nPi5ZI/AAAAAAAAANg/Q-ljQvsvEZ0/s1600/java-example.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="175" src="http://4.bp.blogspot.com/_9g_SKM27_UE/S-Hh4nPi5ZI/AAAAAAAAANg/Q-ljQvsvEZ0/s400/java-example.gif" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;Zaciekawiła ich konkretnie linijka&lt;br /&gt;
&lt;blockquote&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;library.lend(sampleBook).to(reader);&lt;/span&gt;&lt;/blockquote&gt;No i faktycznie, nie dziwię się, że ich zaciekawiła, bo nie jest to wyrażenie typowe dla Javy. Napisałem je w dokumentacji dość bezmyślnie, po prostu tak myślałem o tym kawałku kodu. To&amp;nbsp;bardzo czytelne i jasne wyrażenie. &amp;nbsp;Z drugiej strony jest ono dość trudne do zrealizowania i jego implementacja może sama nie być już taka czytelna, głównie dlatego, że logika pożyczania książki musi być wywołana w metodzie &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;to&lt;/span&gt; a nie &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;lend&lt;/span&gt;.&lt;br /&gt;
&lt;br /&gt;
Ale zacznijmy od początku. &lt;b&gt;Płynne interfejsy&lt;/b&gt;&amp;nbsp;(fluent interfaces) to technika skupiająca się na takim zaprojektowaniu klas, by możliwe było ich wywoływanie w możliwie najczytelniejszy sposób. Żeby czytało się je prawie jak zwykłe zdania (to jest łatwiejsze w językach o elastyczniejszej składni jak ruby, groovy czy scala - w javie składnia jest bardzo sztywna). Powyższy przykład dość dobrze to obrazuje. W prostym przypadku płynny interfejs to tylko ciąg metod z których każda zwraca &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;this&lt;/span&gt;. Tak na przykład tworzy się &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;builder&lt;/span&gt;'y:&lt;br /&gt;
&lt;blockquote&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;new Person()&lt;br /&gt;
.withName("Paweł")&lt;br /&gt;
.withHeight(178)&lt;br /&gt;
.withBirthdayOn(OCTOBER, 23);&lt;/span&gt;&lt;/blockquote&gt;Takie podejście pozwala obejść problem istnienia wielu różnych konstruktorów dla różnych wersji parametrów. Co więcej pozwala rozwijać klasę (dodawać nowe pola) bez zmiany dotychczasowego kodu. Ryzyko tylko jest takie, że możemy w jakichś metodach bazować na nowo dodanych polach, ale od czego są testy ;-)&lt;br /&gt;
Problem z płynnymi interfejsami jest taki, że języki umożliwiające wyłącznie statyczne definiowanie metod raczej wspierają bardziej 'funkcyjne' podejście, gdzie metoda to funkcja operująca na swoich (pewnie paru) parametrach. W przypadku płynnych interfejsów na wykonanie jednej funkcji składa się często parę wywołań metod, z których więkość tylko zmienia stan (magiczne słowo) obiektu/wywołania a dopiero ostatnia w łańcuchu (&lt;i&gt;method chaining&lt;/i&gt;) faktycznie wykonuje jakąś operację. W powyższym przykładzie więc metoda &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;lend&lt;/span&gt; wyłącznie ustawi w jakimś polu klasy library &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;sampleBook&lt;/span&gt;, które będzie użyte potem w metodzie &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;to&lt;/span&gt; jako parametr dla faktycznej operacji pożyczenia książki.&lt;br /&gt;
&lt;br /&gt;
No dobra, ale metoda o nazwie &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;to&lt;/span&gt; może pewnie obsługiwać dużo wiecej operacji - np:&lt;br /&gt;
&lt;blockquote&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;library.send(someBook).to(emailAddress);&lt;/span&gt;&lt;/blockquote&gt;albo&lt;br /&gt;
&lt;blockquote&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;library.move(otherBook).to(poetryShelf);&lt;/span&gt;&lt;/blockquote&gt;Jak obsłużyć takie sytuacje?&lt;br /&gt;
Najprostsze rozwiązanie to przeciążenie metody &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;to&lt;/span&gt;. W zależności od typu parametru będziemy wywoływać tak naprawdę różne &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;to&lt;/span&gt;, dzięki czemu możemy oddzielić ładnie te operacje.&lt;br /&gt;
&lt;br /&gt;
No dobra, wcale nie ładnie - nazwa metody &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;to&lt;/span&gt; nic nam nie mówi o tym co ta metoda naprawdę będzie robić, więc rozróżnianie jej typem parametru zmniejsza tylko czytelność tego kodu. Trochę lepszym rozwiązaniem jest delegacja z metody to do jakiejś metody o nazwie konkretnie definiującej funkcjonalność (np. przez&amp;nbsp;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;doSend(someBook, emailAddress)&lt;/span&gt;). To trochę lepsze rozwiązanie, ale dalej niewiele daje czytelnikowi API. Posiadanie w klasie &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;library&lt;/span&gt;&amp;nbsp;trzech różnych metod &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;to&lt;/span&gt; nie jest czystym rozwiązaniem tego problemu. Tak czy siak pierwsza implementacja naszej biblioteki wygląda następująco:&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/_9g_SKM27_UE/S-HtpCiVhlI/AAAAAAAAAN4/EsiJfCkccTg/s1600/lib2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="640" src="http://1.bp.blogspot.com/_9g_SKM27_UE/S-HtpCiVhlI/AAAAAAAAAN4/EsiJfCkccTg/s640/lib2.png" width="627" /&gt;&lt;/a&gt;&lt;/div&gt;Metoda &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;to &lt;/span&gt;powinna mieć jeszcze weryfikację stanu - nie można wywołać jej samej, bo to nie ma sensu. Dorzucę więc zaraz walidację stanu (sprawdzenie czy ustawiona została operacja).&lt;br /&gt;
Sam kod zbyt śliczny nie jest - już na pierwszy rzut oka widać, że metoda &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;to&lt;/span&gt; będzie puchła - przyda się zatem mały refactoring:&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/_9g_SKM27_UE/S-Hw22r82XI/AAAAAAAAAOA/KIT0vqMMndE/s1600/lib3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="584" src="http://3.bp.blogspot.com/_9g_SKM27_UE/S-Hw22r82XI/AAAAAAAAAOA/KIT0vqMMndE/s640/lib3.png" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;No, teraz dużo lepiej. Metoda &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;to&lt;/span&gt; jest już dość abstrakcyjna - jedyne co robi to weryfikacja stanu (czy może być wywołana), dodanie jej parametru (celu operacji) i wywołanie odpowiedniej metody. Nie mamy warunków, a cała logika odpowiedzialna za operacje jest w podklasach operacji (tu magia enumów, ale można było zastosować oczywiście klasyczną strategię). Teraz dodanie kolejnych metod to (dla innych parametrów) oraz innych podobnych metod będzie tylko podobną delegacją do odpowiedniej logiki. Będzie jasne jak i gdzie je dodawać. Jestem zadowolony.&lt;br /&gt;
&lt;br /&gt;
Ale zaraz, powiedziałby Bartek Zdanowski, a co z równoległym dostępem do klasy &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;library&lt;/span&gt;?&lt;br /&gt;
Hm, no to nie takie trudne chyba. Wystarczy wsadzić operację i parametry do &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;ThreadLocal&lt;/span&gt;'a i tak operować na nich. Spróbujmy (klasę &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;Operations&lt;/span&gt;&amp;nbsp;sie nie zmieniła,&amp;nbsp;więc skonwertowałem ją do top-level i jej nie widać, za to widać resztę):&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/_9g_SKM27_UE/S-H1rThwiTI/AAAAAAAAAOY/Dq3Jwc-Jabg/s1600/lib4.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="574" src="http://1.bp.blogspot.com/_9g_SKM27_UE/S-H1rThwiTI/AAAAAAAAAOY/Dq3Jwc-Jabg/s640/lib4.png" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
Jak widać specjalnie nam to kodu nie skomplikowało, a mamy pewny równoległy dostęp. Dorzuciłem przy okazji czyszczenie stanu wywołania, tak, żeby następujące po sobie wywołania na pewno nie miały na siebie wpływu.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Jak widać płynne interfejsy nie są takie straszne, za to mają ogromny wpływ na czytelność kodu. Tak jak każdej innej techniki nie ma ich co nadużywać, ale w przypadku często używanego i/lub złożonego API są one bardzo przydatne. W bardziej skomplikowanych przypadkach może się okazać, że będziemy musieli wprowadzić wzorzec &lt;i&gt;state&lt;/i&gt; by ładnie obsługiwać różne stany obiektu, choć zwykle jeśli to jest niezbędne warto się zastanowić, czy nasza klasa nie zaczyna odbiegać trochę od swojej głównej funkcjonalności.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5009385396224025678-3753299084369288232?l=studiopragmatists.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/StudioPragmatists/~4/q77QkXWZLMY" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://studiopragmatists.blogspot.com/feeds/3753299084369288232/comments/default" title="Komentarze do posta" /><link rel="replies" type="text/html" href="http://studiopragmatists.blogspot.com/2010/05/pynne-interfejsy.html#comment-form" title="Komentarze (12)" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5009385396224025678/posts/default/3753299084369288232?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5009385396224025678/posts/default/3753299084369288232?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/StudioPragmatists/~3/q77QkXWZLMY/pynne-interfejsy.html" title="Płynne interfejsy" /><author><name>Paweł Lipiński</name><uri>http://www.blogger.com/profile/02772675014904905654</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="02130276274143705547" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://4.bp.blogspot.com/_9g_SKM27_UE/S-Hh4nPi5ZI/AAAAAAAAANg/Q-ljQvsvEZ0/s72-c/java-example.gif" height="72" width="72" /><thr:total>12</thr:total><feedburner:origLink>http://studiopragmatists.blogspot.com/2010/05/pynne-interfejsy.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CkQEQng_fCp7ImA9WxFRFUo.&quot;"><id>tag:blogger.com,1999:blog-5009385396224025678.post-7762168400456745706</id><published>2010-04-29T21:45:00.000+02:00</published><updated>2010-04-29T21:45:03.644+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-04-29T21:45:03.644+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="tdd" /><category scheme="http://www.blogger.com/atom/ns#" term="bdd" /><category scheme="http://www.blogger.com/atom/ns#" term="tumbler" /><title>Tumbler</title><content type="html">To, że jestem fanem TDD wie pewnie każdy kto czyta ten blog. Za szczególnie dobrą jego formę uznaję BDD. W kodzie, który powstaje u nas w firmie staramy się stosować to podejście - nazewnictwo wyrażające intencję twórcy, testy pisane w formie przykładów (często z wykorzystaniem promowanego przez Szczepana Fabera szablonu //given //when //then), skupienie na celu powstawania kodu. Niedawno do naszego zespołu projektowego dołączyli nowi programiści (pracownicy naszego klienta) nieposiadający doświadczenia w TDD. Pierwsze o co zapytali to szczegółowa dokumentacja kodu. Nasze przekonywanie, że testy są kompletne i dokładnie dokumentują kod nie spotkały się ze specjalnym zrozumieniem z ich strony ;-) Doszedłem więc do wniosku, że gdybyśmy mogli wygenerować z naszych testów dokumentację - nawet tylko w formie listy funkcjonalności oferowanych przez nasz kod - to pewnie ich opory byłyby znacząco mniejsze. A że akurat prowadziłem tygodniowe szkolenie poza Warszawą (miałem więc codziennie parę wolnych godzin po pracy), postanowiłem napisać narzędzie, które pomogłoby w tworzeniu testów tak, by:&lt;br /&gt;
&lt;div&gt;&lt;ol&gt;&lt;li&gt;jeszcze wyraźniej dawały znać co robią - tak, by nawet niewprawione w TDD oko rozpoznało o co chodzi (po prostu wystarczy czytać zdania...)&lt;/li&gt;
&lt;li&gt;dawały się generować w formie specyfikacji&lt;/li&gt;
&lt;li&gt;pozwalały lepiej myśleć o kodzie zanim jeszcze on powstanie (zapomniana faza 'think' z TDD) - w idealnym przypadku pozwalały na przygotowanie całego zbioru przykładów najpierw - np. wraz z kimś z biznesu, implementację pozostawiając na później (klasyczne Acceptance-Test Driven Development)&lt;/li&gt;
&lt;li&gt;dawały aktualną informacje o stanie implementacji w formie raportu (które funkcjonalności jeszcze nie są zaimplementowane, które są i działają, które nie działają)&lt;/li&gt;
&lt;li&gt;dawało się generować testy z pełnotekstowego opisu funkcjonalności&lt;/li&gt;
&lt;li&gt;wspierały IDE tak, by było tam widać całe zdania opisu a nie nazwy metod testowych&lt;/li&gt;
&lt;/ol&gt;Gdy wracałem ze szkolenia podstawowe funkcje były już prawie gotowe. Samolot nie odleciał (Eyafjoell...) więc miałem do tego 7 godzin w pociągu ;-) I tak powstał &lt;a href="http://tumbler-glass.googlecode.com/"&gt;Tumbler&lt;/a&gt; - biblioteka wspomagająca myślenie o testach jako o przykładach, służąca do prowadzenia myślenia o tym CO zrobić a nie JAK. Nie będę tu duplikował &lt;a href="http://tumbler-glass.googlecode.com/svn/tags/tumbler-0.1.1/apidocs/tumbler/Tumbler.html"&gt;dokumentacji&lt;/a&gt;, zainteresowanym polecam ją przejrzeć - nie jest tego dużo.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div&gt;Chętnie wysłucham komentarzy i pomysłów co można by lepiej/bardziej/na dodatek.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5009385396224025678-7762168400456745706?l=studiopragmatists.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/StudioPragmatists/~4/KSUlHsFrk0A" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://studiopragmatists.blogspot.com/feeds/7762168400456745706/comments/default" title="Komentarze do posta" /><link rel="replies" type="text/html" href="http://studiopragmatists.blogspot.com/2010/04/tumbler.html#comment-form" title="Komentarze (6)" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5009385396224025678/posts/default/7762168400456745706?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5009385396224025678/posts/default/7762168400456745706?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/StudioPragmatists/~3/KSUlHsFrk0A/tumbler.html" title="Tumbler" /><author><name>Paweł Lipiński</name><uri>http://www.blogger.com/profile/02772675014904905654</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="02130276274143705547" /></author><thr:total>6</thr:total><feedburner:origLink>http://studiopragmatists.blogspot.com/2010/04/tumbler.html</feedburner:origLink></entry><entry gd:etag="W/&quot;C0ENQ3k6eip7ImA9WxFSF0U.&quot;"><id>tag:blogger.com,1999:blog-5009385396224025678.post-5436137882868786176</id><published>2010-04-20T18:41:00.000+02:00</published><updated>2010-04-20T18:41:32.712+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-04-20T18:41:32.712+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="tdd" /><title>Uczenie TDD</title><content type="html">Cały zeszły tydzień byłem we Wrocławiu i prowadziłem szkolenie a potem coaching w zakresie TDD. Przypomniałem tam sobie jak bardzo lubię uczyć ludzi TDD. To szczególny czas. Gdy zaczynamy szkolenie większość uczestników to całkiem przyzwoici programiści. Często z paroletnim doświadczeniem, paroma lub nawet paronastoma większymi lub mniejszymi projektami za sobą. Są wśród nich tacy, dla których programowanie to tylko praca, są też tacy, którzy spędzają tak każdą wolną chwilę. Są przekonani, że TDD to dobry pomysł, ale są też sceptycy przychodzący sprawdzić kto ma rację. Dzięki temu takie szkolenia to dla mnie wyzwania by spełnić wszystkich oczekiwania, wymagające kreatywności i dobrego przygotowania, pozwalające mi wciąż się uczyć i poprawiać swój warsztat. Szkolenia z TDD to jednak głównie okazja dla uczestników, by z dobrych programistów stać się świetnymi. To oczywiście nie staje się w dzień ani tydzień, ale takie szkolenie jest świetnym początkiem takiej zmiany.&lt;br /&gt;
Bardzo lubię patrzeć, jak umiejętności uczestników zmieniają się z ćwiczenia na ćwiczenie. Pierwszy dzień to zwykle poznanie nowych technik. Pierwsze próby myślenia "wspak" - od testów do implementacji. Powoli jednak okazuje się, że takie podejście ma swoje zalety. Najpierw uczestnicy dostrzegają jak wygodnie jest posiadanie testów przy refaktoryzacji. Później doceniają&amp;nbsp;&lt;i&gt;coding by example&lt;/i&gt;&amp;nbsp;- myślenie o funkcjonalnościach w kontekście przykładów użycia kodu. Każde następne ćwiczenie idzie szybciej od poprzedniego, z większym skupieniem na tym o &lt;i&gt;co&lt;/i&gt;&amp;nbsp;kod ma robić a nie &lt;i&gt;jak.&lt;/i&gt;&lt;br /&gt;
Fajnie jest patrzeć jak taki sposób tworzenia kodu, początkowo idący dość ciężko, pod koniec szkolenia nie sprawia już uczestnikom takich problemów, daje za to dużo satysfakcji. Patrzeć jak dużo można nauczyć się w tak krótkim czasie i o jak wiele można poprawić swój warsztat w ciągu tych paru dni. Dla mnie to przyjemne doświadczenie kontaktu z ludźmi, którzy &lt;i&gt;chcą&lt;/i&gt;&amp;nbsp;się uczyć i okazja do poprawiania umiejętności trenerskich. A na sam koniec to nieskromne uczucie zadowolenia gdy przeglądam wyniki ankiet od uczestników ;-)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5009385396224025678-5436137882868786176?l=studiopragmatists.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/StudioPragmatists/~4/QrtyS927mWk" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://studiopragmatists.blogspot.com/feeds/5436137882868786176/comments/default" title="Komentarze do posta" /><link rel="replies" type="text/html" href="http://studiopragmatists.blogspot.com/2010/04/uczenie-tdd.html#comment-form" title="Komentarze (2)" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5009385396224025678/posts/default/5436137882868786176?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5009385396224025678/posts/default/5436137882868786176?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/StudioPragmatists/~3/QrtyS927mWk/uczenie-tdd.html" title="Uczenie TDD" /><author><name>Paweł Lipiński</name><uri>http://www.blogger.com/profile/02772675014904905654</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="02130276274143705547" /></author><thr:total>2</thr:total><feedburner:origLink>http://studiopragmatists.blogspot.com/2010/04/uczenie-tdd.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CUYMRno9cSp7ImA9WxFTGE4.&quot;"><id>tag:blogger.com,1999:blog-5009385396224025678.post-8344504544252969438</id><published>2010-04-09T19:13:00.000+02:00</published><updated>2010-04-09T19:13:07.469+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-04-09T19:13:07.469+02:00</app:edited><title>Retrospektywa po AgileCE</title><content type="html">Pierwsze&amp;nbsp;&lt;a href="http://agilece.com/"&gt;AgileCE&lt;/a&gt; jest już historią. Kto nie był, niech żałuje. Poziom merytoryczny tej konferencji był naprawdę wysoki. Ale po kolei.&lt;br /&gt;
&lt;br /&gt;
Co się udało:&lt;br /&gt;
&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;Świetne miejsce, idealnie dopasowane do liczby osób&lt;/li&gt;
&lt;li&gt;Paul Klipp, organizator, reagował natychmiastowo i efektywnie na wszystkie niespodzianki&lt;/li&gt;
&lt;li&gt;Były naprawdę ciekawe rozmowy kuluarowo-openspace'owe&lt;/li&gt;
&lt;li&gt;Byli świetni prelegenci&lt;/li&gt;
&lt;li&gt;Nie było tłumu&lt;/li&gt;
&lt;li&gt;Kawa, ciasteczka i owoce dostępne cały czas&lt;/li&gt;
&lt;li&gt;Spotkałem dużo interesujących ludzi, spędziłem masę czasu na rozmowach&lt;/li&gt;
&lt;li&gt;Inicjatywa Agile Warsaw&lt;/li&gt;
&lt;li&gt;Masa inspiracji i myśli&lt;/li&gt;
&lt;li&gt;Świetne piwo w nocy&lt;/li&gt;
&lt;/ul&gt;Co można poprawić w przyszłym roku:&lt;br /&gt;
&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;inna restauracja na obiad dla prelegentów - czekanie 2.5 godziny na obiad i 1.5 na piwo to troszeczkę za dużo, ale może to jakaś Krakowska tradycja ;-)&lt;/li&gt;
&lt;li&gt;openspace powinien być &amp;nbsp;dodatkowo a nie w trakcie wykładów - ciężko zrezygnować z fajnego wykładu na rzecz fajnej rozmowy&lt;/li&gt;
&lt;li&gt;może jeszcze parę osób ze stolicy by się wybrało?&amp;nbsp;&lt;/li&gt;
&lt;li&gt;poza proponowaniem hotelu można było znaleźć jakiś hostel w okolicy - nie każdy chce wydać na poleżenie w łóżku przez parę godzin więcej niż za dwudniową konferencję... można by wtedy wracać "w kupie" z nocnego piwa&lt;/li&gt;
&lt;/ul&gt;A teraz na czym byłem:&lt;br /&gt;
&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;Keynote w wykonaniu Rachel Davies&lt;br /&gt;
Rachel mówiła o ważności retrospektyw w życiu zespołu - ich wpływie na jakość produktów, motywację zespołu. Ważna rzecz dla nas: akcje z retrospektywy powinny mieć właściciela i ew. czas wykonania. Dzięki temu zespół o nich nie zapomni. Rachel mówiła też o wliczaniu akcji z retrospektyw do backlogu, by były widoczne i liczone do prędkości zespołu. Mówiła też o ich wycenianiu, ale to mnie nie przekonało - wiele z naszych akcji nie sposób wyrazić w żadnej mierze złożoności.&lt;/li&gt;
&lt;li&gt;ScrumFluenca pana Jensa Korte. Jens przedstawił główne myśli i źródła, które wpłynęły na powstanie Scrum'a. Zrobił OGROMNĄ mapę zależności, która jest faktycznie bardzo ciekawa. Niestety Jens nie dał rady językowo i ciężko go było słuchać. No i może nie do końca dla mnie była ta prezentacja, bo trochę się znudziłem.&lt;/li&gt;
&lt;li&gt;Monika Konieczny mówiła o ważności komunikacji w zespołach programistycznych. Brałem udział w symulacji pieczenia tortu z niejasnymi wymaganiami co faktycznie było fajne i w amerykańskim stylu prowadzenia prezentacji. Monika mówiła o altergames - grach symulacyjnych, które mogą pomóc zespołowi w zrozumieniu problemu. Ciekawe i przyjemne w słuchaniu. Szkoda, że Monika nie odniosła się do innych ruchów związanych z grami w światku agile'owym (np. &lt;a href="http://agilefairytales.com/games.html"&gt;agile fairytales&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;Potem gadałem z &lt;a href="http://blog.brodzinski.com/"&gt;Pawłem Brodzińskim&lt;/a&gt;&amp;nbsp;w temacie agile: mindset or just a toolbox. Chyba nikt nikogo nie przekonał, ale przy okazji poruszyliśmy chyba z 20 innych tematów - było naprawdę ciekawie.&lt;/li&gt;
&lt;li&gt;Ranek następnego dnia zajęła Zuzana Sochova z tematem kultury agile'owej w organizacjach, które tak tworzą oprogramowanie.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;Potem głównie rozmawiałem na korytarzu i w openspace'sie o tworzeniu jakiejś inicjatywy zwinnej w Warszawie, &lt;a href="http://accde10.pbworks.com/"&gt;Agile Coach Camp&lt;/a&gt; na który się wybieram na przełomie kwietnia i maja i parę innych tematów.&lt;/li&gt;
&lt;li&gt;Ostatnim wykładem był The Sword and Other Tales w wykonaniu dwóch panów z Anglii, w czasie którego mówili o praktykach w radzeniu sobie z różnymi problemami u nich w zespole. Cała prezentacja była w stylu low-tech (slajdami były zdjęcia z obrazków na whiteboardzie). Panowie cały czas grali (w sensie teatralnym) i prezentacja była naprawdę wciągająca.&lt;/li&gt;
&lt;/ul&gt;Całość oceniam zdecydowanie wysoko. Zarówno merytorycznie jak i organizacyjnie. Gratulacje dla organizatorów! Już czekam na przyszły rok.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5009385396224025678-8344504544252969438?l=studiopragmatists.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/StudioPragmatists/~4/d8lvpGbc1BI" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://studiopragmatists.blogspot.com/feeds/8344504544252969438/comments/default" title="Komentarze do posta" /><link rel="replies" type="text/html" href="http://studiopragmatists.blogspot.com/2010/04/retrospektywa-po-agilece.html#comment-form" title="Komentarze (1)" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5009385396224025678/posts/default/8344504544252969438?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5009385396224025678/posts/default/8344504544252969438?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/StudioPragmatists/~3/d8lvpGbc1BI/retrospektywa-po-agilece.html" title="Retrospektywa po AgileCE" /><author><name>Paweł Lipiński</name><uri>http://www.blogger.com/profile/02772675014904905654</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="02130276274143705547" /></author><thr:total>1</thr:total><feedburner:origLink>http://studiopragmatists.blogspot.com/2010/04/retrospektywa-po-agilece.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CkIER3w9cSp7ImA9WxFTFEQ.&quot;"><id>tag:blogger.com,1999:blog-5009385396224025678.post-2367622588289959092</id><published>2010-04-05T20:01:00.000+02:00</published><updated>2010-04-05T20:01:46.269+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-04-05T20:01:46.269+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="szkolenie tdd" /><title>AgileCE i ostatni tydzień rejestracji na szkolenie TDD</title><content type="html">Pod koniec tego tygodnia odbędzie się w Krakowie &lt;a href="http://agilece.com/"&gt;konferencja Agile Central Europe&lt;/a&gt;&amp;nbsp;(w skrócie AgileCE). Będę miał przyjemność opowiedzieć tam przy okazji wykładu &lt;i&gt;Being an agile nearshore team&amp;nbsp;&lt;/i&gt;o tym jak budowaliśmy nasz zespół, wprowadzaliśmy zwinne praktyki, o zdarzeniach jakie mieliśmy w relacjach z klientem i jak sobie z nimi radziliśmy. Wszystkich zainteresowanych serdecznie zapraszam!&lt;br /&gt;
&lt;br /&gt;
Ponadto rozpoczęliśmy właśnie ostatni tydzień przed zamknięciem rejestracji na szkolenie TDD. Wszystkich chętnych poznania technik związanych z tworzeniem oprogramowania przy pomocy testów zapraszamy. Gwarantujemy ogrom wiedzy i masę ćwiczeń!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5009385396224025678-2367622588289959092?l=studiopragmatists.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/StudioPragmatists/~4/waaH7atnU_Y" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://studiopragmatists.blogspot.com/feeds/2367622588289959092/comments/default" title="Komentarze do posta" /><link rel="replies" type="text/html" href="http://studiopragmatists.blogspot.com/2010/04/agilece-i-ostatni-tydzien-rejestracji.html#comment-form" title="Komentarze (0)" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5009385396224025678/posts/default/2367622588289959092?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5009385396224025678/posts/default/2367622588289959092?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/StudioPragmatists/~3/waaH7atnU_Y/agilece-i-ostatni-tydzien-rejestracji.html" title="AgileCE i ostatni tydzień rejestracji na szkolenie TDD" /><author><name>Paweł Lipiński</name><uri>http://www.blogger.com/profile/02772675014904905654</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="02130276274143705547" /></author><thr:total>0</thr:total><feedburner:origLink>http://studiopragmatists.blogspot.com/2010/04/agilece-i-ostatni-tydzien-rejestracji.html</feedburner:origLink></entry><entry gd:etag="W/&quot;Dk8BRXs_fyp7ImA9WxFTFEs.&quot;"><id>tag:blogger.com,1999:blog-5009385396224025678.post-4554884585746106701</id><published>2010-04-02T09:50:00.000+02:00</published><updated>2010-04-05T12:54:14.547+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-04-05T12:54:14.547+02:00</app:edited><title>Święta</title><content type="html">Na te wiosenne święta życzymy Wam wszystkim chwili rozkojarzenia i odpoczynku.&lt;br /&gt;
&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;,`.&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; ,'` | &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;_.-.&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp;,` &amp;nbsp; | &amp;nbsp; &amp;nbsp; &amp;nbsp;,',' /&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp;: &amp;nbsp; &amp;nbsp;| &amp;nbsp; &amp;nbsp;,',' &amp;nbsp;;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; \ &amp;nbsp; : &amp;nbsp; / / &amp;nbsp; /&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;\ &amp;nbsp; `.' ( &amp;nbsp;,'&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; ,'' &amp;nbsp; &amp;nbsp; _ &amp;nbsp;`.&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; ,' &amp;nbsp; &amp;nbsp; &amp;nbsp;(o_) &amp;nbsp;`\&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;&amp;nbsp;&amp;nbsp;. (,.) &amp;nbsp; _.-- &amp;nbsp; &amp;nbsp; :&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;-..`/( &amp;nbsp;.-'_..- &amp;nbsp; &amp;nbsp;`|&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;&amp;nbsp;.-'\,`. `-._ &amp;nbsp; &amp;nbsp; &amp;nbsp; ;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; `._ &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; /__&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; ,':)-.._ &amp;nbsp; _.(:::`.&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; |'\ &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; / /`:::|&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;&amp;nbsp;&amp;nbsp; ,' \ : &amp;nbsp; &amp;nbsp; &amp;nbsp; : : &amp;nbsp; `:|&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;&amp;nbsp;&amp;nbsp;/ &amp;nbsp; : | &amp;nbsp; &amp;nbsp; &amp;nbsp; | | &amp;nbsp; &amp;nbsp; \&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;&amp;nbsp;: &amp;nbsp; &amp;nbsp;| | &amp;nbsp; &amp;nbsp; &amp;nbsp; : :..---.:&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;&amp;nbsp;| &amp;nbsp; &amp;nbsp;| ; &amp;nbsp; &amp;nbsp; &amp;nbsp; ,`._`-.|_ `.&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;&amp;nbsp;| &amp;nbsp; &amp;nbsp;|' &amp;nbsp; &amp;nbsp; &amp;nbsp;,'._ &amp;nbsp;`. `. |_\&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;&amp;nbsp;| &amp;nbsp; &amp;nbsp;: &amp;nbsp; &amp;nbsp; &amp;nbsp;/`-. `. &amp;nbsp;`. `. &amp;nbsp;:&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;&amp;nbsp;: &amp;nbsp; &amp;nbsp; \ &amp;nbsp; &amp;nbsp;: __java.lang.IndexOutOfBoundsException: Index: 0, Size: 0&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;&amp;nbsp;`. `. &amp;nbsp;`. \ ;at java.util.ArrayList.BunnyCheck&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;&amp;nbsp;&amp;nbsp;\ &amp;nbsp; &amp;nbsp; \ &amp;nbsp; |. &amp;nbsp;/ at javax.swing.AbstractBunny.fireActionPerformed&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;&amp;nbsp;&amp;nbsp;|\ &amp;nbsp; &amp;nbsp; `.at javax.swing.plaf.basic.BunnyHandler.: `. __ &amp;nbsp;\ \ &amp;nbsp; /&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;&amp;nbsp;&amp;nbsp;' ` at java.awt.Component.processBunnyEvent .:::::\ &amp;nbsp;`. / &amp;nbsp;\ \,'&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;.::::::::::-..'_..-'&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;&lt;br /&gt;
&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;&lt;br /&gt;
&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;Uuuups! Przepraszamy za królika. U nas działał. Widać trzeba go było jednak zrobić za pomocą TDD...&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5009385396224025678-4554884585746106701?l=studiopragmatists.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/StudioPragmatists/~4/h_cbTfuxTMI" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://studiopragmatists.blogspot.com/feeds/4554884585746106701/comments/default" title="Komentarze do posta" /><link rel="replies" type="text/html" href="http://studiopragmatists.blogspot.com/2010/04/swieta.html#comment-form" title="Komentarze (0)" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5009385396224025678/posts/default/4554884585746106701?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5009385396224025678/posts/default/4554884585746106701?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/StudioPragmatists/~3/h_cbTfuxTMI/swieta.html" title="Święta" /><author><name>Paweł Lipiński</name><uri>http://www.blogger.com/profile/02772675014904905654</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="02130276274143705547" /></author><thr:total>0</thr:total><feedburner:origLink>http://studiopragmatists.blogspot.com/2010/04/swieta.html</feedburner:origLink></entry><entry gd:etag="W/&quot;A0UMQXs9fip7ImA9WxBaGUs.&quot;"><id>tag:blogger.com,1999:blog-5009385396224025678.post-7551366075919540797</id><published>2010-03-27T14:23:00.000+01:00</published><updated>2010-03-30T19:14:40.566+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-03-30T19:14:40.566+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="tdd" /><title>TDD interfejsu użytkownika</title><content type="html">&lt;blockquote&gt;&lt;i&gt;29.03.2010   Zmieniłem trochę kod zgodnie z podpowiedzią Jakuba Nabrdalika, żeby zapisać to mockitową składnią BDD, oraz Michała Margiela, który znalazł duplikację w kodzie :-)&lt;/i&gt;&lt;/blockquote&gt;Pamiętam czas, kiedy miałem 19-20 lat. W ciągu dnia studia, wieczorami dla zabawy tłukłem oldskoolowe dos'owe wirusy i animacje fraktalne w asemblerze, a nad ranem i w weekendy zarabiałem, żeby było w tygodniu na obiady w 'Złotej Kurce' koło polibudy. Zarabiałem pisząc aplikacje w Delphi i C++ Builderze. To były narzędzia! Wystarczyło poukładać okienkowe komponenty na panelu i na każdym wykonać dwuklik. Od razu IDE przenosiło Cię do odpowiedniej metody gdzie wystarczyło napisać kod, który miał być wykonany gdy użytkownik kliknie w ten komponent. Ciągle się zastanawiam dlaczego mimo upływu 12 lat, środowiska Javowe nie pozwalają na to samo...&lt;br /&gt;
Ale wracając do tematu - za pomocą tych narzędzi: świetnego środowiska i tzw. Event-Driven Development stworzyłem parę... potworków. Dlaczego potworków? No więc pewnie dlatego, że moje doświadczenie programistyczne było małe i nie wiedziałem, że kod UI nie powinien być wymieszany z logiką. A już na pewno nie z kodem obsługi bazy danych...&lt;br /&gt;
W międzyczasie parę lat minęło i zdążyłem się zorientować, że są sposoby na pisanie kodu tak, by móc go zmienić bezpiecznie jakiś czas po napisaniu. Najpierw okazało się, że są wzorce projektowe, które dość skutecznie pomagają zaprojektować kod przed napisaniem. Dzięki temu dało się potem taki kod zmienić i poprawić. Niestety zwykle tylko raz, czasem dwa. Potem ten kod i tak stawał się do ... niczego nie podobny. Potem okazało się, że są testy jednostkowe, które pozwalają na utrzymywanie kodu w nieskończoność (o ile utrzymuje się też testy...) I tak doszedłem w końcu do mementu kiedy logikę miałem ujarzmioną testami, ale pozostawał interfejs użytkownika jako najsłabsze ogniwo. Tak mniej-więcej koło 2006 Martin Fowler napisał parę artykułów opisujących wzorce tworzenia UI (w szczególności chodzi mi tu o &lt;i&gt;Passive View&lt;/i&gt;) i... sprawa stała się jasna :-)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
W Pragmatists tworzymy oprogramowanie wyłącznie w TDD. Również interfejs użytkownika. Używamy więc wzorców przedstawionych przez Fowlera, ponieważ podstawowa ich zaleta to uniezależnienie logiki UI od widoku, więc idealnie nadają się do testowania. Nie zawsze jest to dokładnie to, co proponował Fowler, ale co do zasady jest to jakaś wersja wzorca Model-View-Presenter.    &lt;i&gt;Passive View&lt;/i&gt; polega na tym, że widok potrafi jedynie przekazać prezenterowi komunikat o tym, że zaistniało jakieś zdarzenie, oraz potrafi się odświeżyć na podstawie posiadanych przez siebie danych. Prezenter, po otrzymaniu komunikatu, że coś się w widoku zdarzyło, wykonuje odpowiednią operację na modelu i pobiera z niego dane. Następnie ustawia na widoku uaktualnione dane,a ten po zakończeniu działania prezentera, odświeża się. Widok jest zupełnie 'głupi' - bez jakiejkolwiek logiki poza wywoływaniem prezentera i własnym odświeżaniem się. Cała logika widoku jest w prezenterze, a model jest tak naprawdę &lt;i&gt;Business Delegate&lt;/i&gt;'em do reszty aplikacji. Dzięki temu możemy odpuścić sobie testowanie view, bo ryzyko, że pomylimy się w jakimś getterze jest znikome ;-) Możemy dokładnie przetestować prezenter, włącznie z ew. przepływem między ekranami/panelami. A testy modelu to tak naprawdę testy funkcjonalne niższych warstw aplikacji.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Weźmy konkretny przykład. Załóżmy, że piszemy aplikację a'la kalendarz w Outlook'u i mamy ekran tworzenia nowego zdarzenia w kalendarzu. Takie zdarzenie ma zwykle nazwę, datę z godziną i listę zaproszonych osób. Chcielibyśmy móc takie zdarzenie zapisać, wybrać z książki adresowej osoby zaproszone, i wysłać zaproszenia do osób zainteresowanych. W zależności czy aplikację piszemy top-down (zaczynamy od UI idąc 'w głąb') czy mamy już gotową logikę (baza danych + wysyłanie zaproszeń), możemy zacząć albo od prezentera albo od modelu. Załóżmy więc, że zaczynamy od zera, więc jakiś grafik dłubie dżejpegi z ekranem nowego zdarzenia, a my w tym czasie robimy coś ciekawego :-)&lt;br /&gt;
&lt;br /&gt;
Wiemy, że prezenter trzyma sobie model i widok (od razu je mockujemy - przecież ich nie ma, bo zaczynamy od prezentera), i potrafi zapisać zdarzenie (na podstawie danych z widoku, za pośrednictwem modelu):&lt;br /&gt;
&lt;pre class="prettyprint"&gt;@RunWith(MockitoJUnitRunner.class)
public class NewEventPresenterTest {
 @Mock
 private NewEventView view;
 
 @Mock
 private NewEventModel model;

 private NewEventPresenter presenter;

 
 @Before
 public void setUpPresenter() {
  presenter = new NewEventPresenter(model, view);
 }
 
 @Test
 public void shouldSaveEvent() throws Exception {
  //when
  presenter.saveEvent();
  
  //then
  verify(model).saveEvent(presenter.event);  
 }
}
&lt;/pre&gt;&lt;br /&gt;
No i taki test pozwala nam sprawdzić, że prezenter faktycznie zapisze reprezentowane przez view zdarzenie w modelu.&lt;br /&gt;
&lt;br /&gt;
No to teraz użytkownik zmienia nazwę zdarzenia:&lt;br /&gt;
&lt;pre class="prettyprint"&gt;@Test
 public void shouldSetEventTitle() throws Exception {
  //given
  given(view.getTitle()).willReturn("test title");
  
  //when
  presenter.onTitleChanged();
  
  //then
  verify(view).getTitle();    
  assertEquals("test title", presenter.event.getTitle());
 }
&lt;/pre&gt;&lt;br /&gt;
Ustawia datę:&lt;br /&gt;
&lt;pre class="prettyprint"&gt;@Test
 public void shouldSetEventDateTime() throws Exception {
  //given
  given(view.getDateTime()).willReturn("2010-01-01 12:00");
  
  //when
  presenter.onDateTimeChanged();
  
  //then
  verify(view).getDateTime();    
  assertEquals(new LocalDateTime(2010,1,1,12,0), presenter.event.getDateTime());
 }
&lt;/pre&gt;&lt;br /&gt;
Pokazuje użytkownikowi listę osób, z których może wybrać kogoś do zaproszenia:&lt;br /&gt;
&lt;pre class="prettyprint"&gt;@Test
 public void shouldFillPotentialInvitees() throws Exception {
  //when
  presenter.onShowPotentialInvitees();
  
  //then
  verify(model).listPotentialInvitees();
  verify(view).setPotentialInvitees((Collection&lt;string&gt;)anyObject());
 }
&lt;/pre&gt;&lt;br /&gt;
No i w końcu zaprasza wybrane osoby:&lt;br /&gt;
&lt;pre class="prettyprint"&gt;@Test
 public void shouldSetSelectedInvitees() throws Exception {
  //given
  given(view.getSelectedInvitees()).willReturn(Arrays.asList("friend1", "friend2"));
  
  //when
  presenter.onSelectedInvitees();
  
   //then
  verify(model).invitePeople(view.getSelectedInvitees());
  assertEquals(view.getSelectedInvitees(), presenter.event.getInvitedPeople()); 
 }
&lt;/pre&gt;&lt;br /&gt;
&lt;br /&gt;
Do tego mamy oczywiście model, który wykonuje te wszystkie operacje. Najpierw tworzymy sobie zdarzenie (tu mockujemy dalszą warstwę, bo testujemy model):&lt;br /&gt;
&lt;pre class="prettyprint"&gt;@RunWith(MockitoJUnitRunner.class)
public class NewEventModelTest { 
 @Mock
 private EventService eventService;
 
 private Event sampleEvent;

 private NewEventModel eventModel;
 
 @Before
 public void initSampleEvent() {
  Collection&lt;string&gt; friends = Arrays.asList("friend1","friend2");
  sampleEvent = new Event()
   .title("Test Event")
   .dateTime(new LocalDateTime()
    .withDate(2010, 10, 23)
    .withTime(0, 0, 0, 0))
   .invited(friends);
  when(eventService.fetchEvent((LocalDateTime)anyObject())).thenReturn(sampleEvent);
  when(eventService.fetchPotentialInvitees()).thenReturn(Arrays.asList("friend1", "friend2", "friend3"));
  
  eventModel = new NewEventModel(); 
  eventModel.setEventService(eventService);
 }

 @Test
 public void shouldCreateNewEvent() throws Exception {  
  assertNotNull(sampleEvent);
 }
}
&lt;/pre&gt;&lt;br /&gt;
Weryfikujemy warunki brzegowe: &lt;br /&gt;
&lt;pre class="prettyprint"&gt;@Test
 public void shouldNotSendInvitationsWhenNoEvent() throws Exception {
  //given  
  sampleEvent = null;
  
  //when
  eventModel.sendInvitations(sampleEvent);
  
  //then
  verifyZeroInteractions(eventService);
 }

 @Test
 public void shouldNotSendInvitationsWhenInviteesNull() throws Exception {
  //given  
  given(eventService.fetchEvent((LocalDateTime)anyObject())).willReturn(sampleEvent.invited(null));
  
  //when
  eventModel.sendInvitations(sampleEvent);
  
  //then
  verifyZeroInteractions(eventService);
 }
 
 @Test
 public void shouldNotSendInvitationsWhenInviteesEmpty() throws Exception {
  //given  
  given(eventService.fetchEvent((LocalDateTime)anyObject())).willReturn(sampleEvent.invited(new ArrayList&lt;string&gt;()));
  
  //when
  eventModel.sendInvitations(sampleEvent);
  
  //then
  verifyZeroInteractions(eventService);
 }
&lt;/pre&gt;&lt;br /&gt;
Wyciągamy listę potencjalnych zaproszonych: &lt;br /&gt;
&lt;pre class="prettyprint"&gt;@Test
 public void shouldRetrievePossibleInvitees() throws Exception {
  //given  
  NewEventModel eventModel = new NewEventModel();  
  eventModel.setEventService(eventService);  
  
  //when
  Collection&lt;string&gt; possibleInvitees = eventModel.listPotentialInvitees();
  
  //then
  assertNotNull(possibleInvitees);
  assertFalse(possibleInvitees.isEmpty());  
 }
&lt;/pre&gt;&lt;br /&gt;
W końcu wysyłamy zaproszenia:&lt;br /&gt;
&lt;pre class="prettyprint"&gt;@Test
 public void shouldSendInvitationsWhenInviteesNotEmpty() throws Exception {
  //when
  eventModel.sendInvitations(sampleEvent);
  
  //then
  verify(eventService).sendInvitations(sampleEvent.getInvitedPeople());
 }
&lt;/pre&gt;&lt;br /&gt;
I zapisujemy zdarzenie:&lt;br /&gt;
&lt;pre class="prettyprint"&gt;@Test
 public void shouldSaveEvent() throws Exception {
  //when
  eventModel.saveEvent(sampleEvent);
  
  //then
  assertSame(sampleEvent, eventService.fetchEvent(sampleEvent.getDateTime()));
 }
&lt;/pre&gt;&lt;br /&gt;
W ten sposób nie tylko mamy dobrze przetestowany zarówno prezenter jak i model, ale również mamy ładny w nich kod (czyż fluent interface klasy Event nie jest śliczny?) - a to dzięki temu, że zaczęliśmy pisanie tego kodu od jego użycia (testu = przykładu). Teraz tylko czas popoganiać grafika, żeby szybciej te pikselki układał i wybrać jakąś wygodną technologię do napisania widoku...&lt;br /&gt;
&lt;br /&gt;
Ach, zapomniałem wkleić tu właściwy kod.... Ech, z resztą, ten to przecież teraz już każdy potrafi napisać, w końcu eclipse wygenerował sam przynajmniej z 30%...&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
*Autor posta dziękuje autorowi Mockito za to, że dzięki niemu w powyższych przykładach mockowanie jest tak proste, że prawie go nie widać :)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5009385396224025678-7551366075919540797?l=studiopragmatists.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/StudioPragmatists/~4/apg4qstT0rU" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://studiopragmatists.blogspot.com/feeds/7551366075919540797/comments/default" title="Komentarze do posta" /><link rel="replies" type="text/html" href="http://studiopragmatists.blogspot.com/2010/03/tdd-interfejsu-uzytkownika.html#comment-form" title="Komentarze (4)" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5009385396224025678/posts/default/7551366075919540797?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5009385396224025678/posts/default/7551366075919540797?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/StudioPragmatists/~3/apg4qstT0rU/tdd-interfejsu-uzytkownika.html" title="TDD interfejsu użytkownika" /><author><name>Paweł Lipiński</name><uri>http://www.blogger.com/profile/02772675014904905654</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="02130276274143705547" /></author><thr:total>4</thr:total><feedburner:origLink>http://studiopragmatists.blogspot.com/2010/03/tdd-interfejsu-uzytkownika.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DUIAQHs7eyp7ImA9WxBaEkQ.&quot;"><id>tag:blogger.com,1999:blog-5009385396224025678.post-2154434816647245443</id><published>2010-03-22T23:32:00.000+01:00</published><updated>2010-03-22T23:39:01.503+01:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-03-22T23:39:01.503+01:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="tdd" /><title>W TDD nie chodzi o testy</title><content type="html">&lt;div&gt;W trakcie przygotowań do szkolenia z TDD:&lt;/div&gt;&amp;nbsp;&amp;nbsp;K: &lt;i&gt;Trzeba jakoś przekazać uczestnikom szkolenia, że w TDD nie chodzi o testowanie&lt;/i&gt;&lt;br /&gt;
&lt;div&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&amp;nbsp;&amp;nbsp;M: &lt;/span&gt;Jak to nie chodzi o testowanie? To o co chodzi?&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&amp;nbsp;&amp;nbsp;K: &lt;/span&gt;No chodzi o to, że testy są przykładami. Specyfikują to co właściwy kod ma robić.&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;&amp;nbsp;&amp;nbsp;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;M: &lt;/span&gt;No dobra, ale na koniec to i tak jest testowanie.&amp;nbsp;&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;&amp;nbsp;&amp;nbsp;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;P: &lt;/span&gt;No tak, ale możliwość odpalenia testów i zweryfikowania poprawności kodu jest tylko efektem ubocznym. Podstawowym celem jest dobre zrozumienie problemu oraz utworzenie dobrego designu.&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;&amp;nbsp;&amp;nbsp;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;M: &lt;/span&gt;No tak, ale i tak &lt;b&gt;testujemy&lt;/b&gt;&amp;nbsp;a więc chodzi o &lt;b&gt;testowanie&lt;/b&gt;&lt;/i&gt;&lt;br /&gt;
&lt;i&gt;&lt;b&gt;&amp;nbsp;&lt;/b&gt;&amp;nbsp;&lt;/i&gt;P: &lt;i&gt;Racja, ale nawet gdybyśmy mieli po zakończeniu implementacji stracić związane z nią testy, to i tak byśmy chcieli je pisać. Po prostu wartość z dobrego zrozumienia implementowanego zagadnienia i poprawności designu jest już wystarczająca. Możliwość późniejszego przetestowania - safety net - jest tylko dodatkiem do podstawowej wartości.&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;&lt;br /&gt;
&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;Ta trwająca z 15min dyskusja dała mi chwilę do zastanowienia się o co tak naprawdę chodzi w TDD. I choć M ma rację, że piszemy testy - więc testujemy, to nazwy&amp;nbsp;&lt;/span&gt;&lt;span class="Apple-style-span"&gt;test&lt;/span&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&amp;nbsp;oraz&amp;nbsp;&lt;/span&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;TDD są zdecydowanie mylące. Już napiszę dlaczego.&lt;/span&gt;&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;Tworzenie oprogramowania to bardzo skomplikowane zadanie. Wielu z nas doświadcza tego przy prawie każdym wdrożeniu. Albo (mam nadzieję) nawet wcześniej - jak tester dobiera się do aplikacji i znajduje w niej masę błędów. Skąd one się biorą? Oczywiście czasem jest to jakieś drobne lenistwo programistów, ale większość przypadków to niekompletne rozwiązanie jakiegoś problemu lub niezauważenie jakiejś możliwości przepływu w aplikacji. Czyli żeby takich błędów nie było, trzeba by zapewnić dwie rzeczy:&lt;/span&gt;&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;1) dobre i kompletne zrozumienie rozwiązywanego problemu przez programistę&lt;/span&gt;&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;2) zapewnienie poprawnego wykonania aplikacji dla dowolnych parametrów (dowolnego przepływu kontroli w kodzie)&lt;/span&gt;&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;br /&gt;
&lt;/span&gt;&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;TDD jest odpowiedzią na oba te problemy. Jeśli zaczynamy pisanie kodu od jego wyspecyfikowania, czyli podania przykładów jego użycia, nasze zrozumienie zarówno rozwiązywanego problemu jak i samego pisanego kodu jest dużo większe. Po prostu spędzamy więcej czasu na zastanawianiu się nad problemem oraz projektowaniem rozwiązania. Ze względu na to, że w TDD definiujemy przykłady wykorzystania implementowanego kodu, bardzo często TDD nazywa się kodowaniem przez przykłady (coding by example). Na niższym poziomie piszemy po prostu przykłady zachowań obiektów (czyli jak chcemy wykorzystywać kod obiektu z innego obiektu).&amp;nbsp;&lt;/span&gt;&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;Powiedzmy więc, że chcemy napisać kod do obsługi pianina. Zaczynamy więc od reakcji na wciskanie klawiszy:&lt;/span&gt;&lt;/i&gt;&lt;br /&gt;
&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;/span&gt;&lt;/i&gt;&lt;br /&gt;
&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt; &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;@Test&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-tab-span" style="white-space: pre;"&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt; &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;public void shouldPlaySoundWhenKeyPressed() {&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-tab-span" style="white-space: pre;"&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;  &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;// given&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-tab-span" style="white-space: pre;"&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;  &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;Piano piano = new Piano();&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-tab-span" style="white-space: pre;"&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;  &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-tab-span" style="white-space: pre;"&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;  &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;//when&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-tab-span" style="white-space: pre;"&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;  &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;piano.pressKey(Piano.Key.C);&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-tab-span" style="white-space: pre;"&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;  &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-tab-span" style="white-space: pre;"&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;  &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;//then&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-tab-span" style="white-space: pre;"&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;  &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;assertEquals(Piano.Key.C, piano.lastPlayedSound());&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-tab-span" style="white-space: pre;"&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt; &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
Na razie nic specjalnego. Teraz dodajemy pedał:&lt;br /&gt;
&lt;br /&gt;
&lt;span class="Apple-tab-span" style="white-space: pre;"&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt; &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;@Test&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-tab-span" style="white-space: pre;"&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt; &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;public void shouldPlayLongWhenSustainPedalPressed() &amp;nbsp;{&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-tab-span" style="white-space: pre;"&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;  &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;// given&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-tab-span" style="white-space: pre;"&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;  &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;Piano piano = new Piano();&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-tab-span" style="white-space: pre;"&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;  &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-tab-span" style="white-space: pre;"&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;  &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;//when&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-tab-span" style="white-space: pre;"&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;  &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;piano.pressSustain();&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-tab-span" style="white-space: pre;"&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;  &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;piano.pressKey(Piano.Key.C);&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-tab-span" style="white-space: pre;"&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;  &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;waitFor(5).seconds();&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-tab-span" style="white-space: pre;"&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;  &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;piano.releaseSustain();&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-tab-span" style="white-space: pre;"&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;  &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-tab-span" style="white-space: pre;"&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;  &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;//then&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-tab-span" style="white-space: pre;"&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;  &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;assertEquals(5, piano.lastSound().lasted(seconds));&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-tab-span" style="white-space: pre;"&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt; &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;I już widzimy, że klasa piano wymaga refaktoryzacji, więc poprzedni test powinien mieć w &lt;i&gt;assert&lt;/i&gt;:&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/i&gt;&lt;br /&gt;
&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt; &lt;span class="Apple-style-span" style="font-family: inherit;"&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;div style="display: inline !important; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;&amp;nbsp;piano.lastSound().value()&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;br /&gt;
&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt; &lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/i&gt;&lt;br /&gt;
&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/i&gt;&lt;br /&gt;
&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;div style="display: inline !important; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;W ten sposób kształtujemy API tak jak najwygodniej bę&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/span&gt;&lt;/i&gt;&lt;br /&gt;
&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;div style="display: inline !important;"&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/i&gt;&lt;/div&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;div style="display: inline !important; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="display: inline !important;"&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;dzie nam go używać, wymuszając na implementacji właściwy design i poprawiając czytelność kodu (czy &lt;i&gt;waitFor(5).seconds()&lt;/i&gt;&amp;nbsp;nie jest czytelniejsze od &lt;i&gt;try { Thread.sleep(5000); } catch (InterruptedException e) {}&lt;/i&gt;?). Poprawiamy też nasze zrozumienie problemu - zorientowaliśmy się, że dźwięk ma nie tylko wysokość, ale również długość trwania, więc odpowiednio zmieniliśmy design.&lt;/span&gt;&lt;/span&gt;&lt;/i&gt;&lt;/div&gt;&lt;/div&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/span&gt;&lt;/i&gt;&lt;/div&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt; &lt;span class="Apple-style-span" style="font-family: inherit;"&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;div style="display: inline !important; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;To, że &lt;/span&gt;prawdziwe&amp;nbsp;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;TDD pozwala na zapewnienie poprawnego wykonania kodu dla dowolnych parametrów jest jasne... jak się wie co to jest &lt;/span&gt;prawdziwe&lt;/i&gt;&amp;nbsp;TDD ;-) Tak jak definiował je Kent Beck i jak zwykło się go nauczać, krok implementowania testu realizuje się przez napisanie &lt;i&gt;najprostszego kodu, który powoduje, że test przechodzi.&lt;/i&gt;&amp;nbsp;Jak najbardziej "po bożemu" jest więc pisanie trywialnego kodu po to tylko, żeby na drodze małych kroczków &lt;b&gt;wymusić&lt;/b&gt;&amp;nbsp;w kodzie istnienie wyłącznie tego, co jest wymagane i przetestowane. Tak więc dla powyższego przykładu, pierwsza implementacja metody pressKey wyglądałaby tak:&lt;/div&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/i&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt; &lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/span&gt;&lt;/i&gt;&lt;br /&gt;
&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/span&gt;&lt;/i&gt;&lt;br /&gt;
&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;div style="display: inline !important; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;/div&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/i&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;public Key lastPlayedSound() {&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-tab-span" style="white-space: pre;"&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt; &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;return Key.C;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/i&gt;&lt;br /&gt;
&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;&lt;/span&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;br /&gt;
&lt;/span&gt; Do przejścia testu wystarczy, a powoduje, że na pewno nie napiszemy żadnego kodu niepokrytego testami. Właściwa, kompletna implementacja powstanie już pewnie w ciągu następnych minut. Po napisaniu podobnego testu z inną nutą ten test już nie przejdzie i będzie trzeba napisać pełniejsze rozwiązanie. Choć na pierwszy rzut oka takie podejście wydaje się naiwne i bezsensowne, pomaga ono spojrzeć na problem z punktu widzenia zasady YAGNI - implementujemy wyłącznie to, czego wymaga od nas specyfikacja - czyli testy. W ten sposób łatwiej nam wpaść na prostsze rozwiązania.&lt;br /&gt;
&lt;br /&gt;
TDD to więc prowadzenie naszego rozumowania i rozumienia problemu. To tworzenie sobie w głowie modelu implementowanej dziedziny. To również sposób na dbanie o jakość kodu z punktu widzenia design'u (czytelność, bo implementujemy tak jak chcemy wykorzystywać i na bieżąco refaktoryzujemy by nam jak najbardziej pasowało) oraz poprawności (prowadzenie przykładami wszystkich możliwych ścieżek wykonania kodu).&lt;br /&gt;
To, że za jakiś czas będziemy mogli spokojnie zmienić dowolną część tego kodu, pewni, że jeśli coś zepsujemy, to odpowiedni test nas o tym poinformuje, jest tylko efektem ubocznym. Miłym dodatkiem do poprawnego kodu. A mówili, że nie istnieje coś takiego jak darmowy lunch...&lt;br /&gt;
&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/i&gt;&lt;br /&gt;
&lt;/span&gt;&lt;/i&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5009385396224025678-2154434816647245443?l=studiopragmatists.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/StudioPragmatists/~4/cdEWozRN34k" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://studiopragmatists.blogspot.com/feeds/2154434816647245443/comments/default" title="Komentarze do posta" /><link rel="replies" type="text/html" href="http://studiopragmatists.blogspot.com/2010/03/w-tdd-nie-chodzi-o-testy.html#comment-form" title="Komentarze (2)" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5009385396224025678/posts/default/2154434816647245443?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5009385396224025678/posts/default/2154434816647245443?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/StudioPragmatists/~3/cdEWozRN34k/w-tdd-nie-chodzi-o-testy.html" title="W TDD nie chodzi o testy" /><author><name>Paweł Lipiński</name><uri>http://www.blogger.com/profile/02772675014904905654</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="02130276274143705547" /></author><thr:total>2</thr:total><feedburner:origLink>http://studiopragmatists.blogspot.com/2010/03/w-tdd-nie-chodzi-o-testy.html</feedburner:origLink></entry><entry gd:etag="W/&quot;A0IHRnk7fCp7ImA9WxBbGEw.&quot;"><id>tag:blogger.com,1999:blog-5009385396224025678.post-2524241619134849653</id><published>2010-03-17T08:10:00.000+01:00</published><updated>2010-03-17T10:52:17.704+01:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-03-17T10:52:17.704+01:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="tdd" /><title>Testy end-to-end w TDD</title><content type="html">&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.growing-object-oriented-software.com/cover.jpg"&gt;&lt;img style="float: left; margin: 0pt 10px 10px 0pt; cursor: pointer; width: 240px; height: 319px;" src="http://www.growing-object-oriented-software.com/cover.jpg" alt="" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;div&gt;Steve Freeman i Nat Pryce napisali książkę, która wywołuje dużo poruszenia w światku wytwarzania oprogramowania. &lt;a href="http://www.growing-object-oriented-software.com/"&gt;Growing Object-Oriented Software Guided by Tests&lt;/a&gt; jest &lt;a href="http://gojko.net/2009/11/20/dark-arts-of-tdd-explained/"&gt;oceniana jako unikalna&lt;/a&gt; - dotychczas takiej książki o TDD nie było. Moim zdaniem w całej dziedzinie tworzenia oprogramowania rzadko spotyka się książki tak praktyczne, z dużą ilością kodu i świetnym przedstawieniem toku myślenia autorów.&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Lektura tej książki skłoniła też Jacka Laskowskiego do napisania (jak na razie) &lt;a href="http://jlaskowski.blogspot.com/search/label/TDD"&gt;dwóch postów&lt;/a&gt;. Chciałbym tutaj skomentować jego wypowiedź z &lt;a href="http://jlaskowski.blogspot.com/2010/03/nie-ma-geniuszu-bez-ziarna-szalenstwa.html"&gt;pierwszego z nich&lt;/a&gt;. Najpierw krótko sparafrazuję myśli Jacka, do których chciałbym się odnieść:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;Jacek pisze, że dotychczas zakładał, że w TDD chodzi o testy jednostkowe, czyli testujące poszczególne klasy w izolacji.&lt;br /&gt;Tymczasem zaskoczyło go proponowane w książce podejście, w którym rozpoczyna się tworzenie aplikacji od napisania przekrojowego testu dotyczącego jednej prostej funkcjonalności. Chodzi o wyspecyfikowanie zachowania aplikacji z uwzględnieniem jej wszystkich warstw, od interfejsu użytkownika do połączeń z zewnętrznymi systemami, zanim jeszcze powstała architektura aplikacji. Jacek zastanawia się, który zespół wytrzyma tworzenie takiego testu, kiedy nic jeszcze nie ma? Wymaga to dużej determinacji i zdyscyplinowania - rzadko spotykanych zalet zespołów programistycznych.&lt;br /&gt;&lt;/blockquote&gt;Co daje nam stosowanie podejścia Test Driven? Jedną z zalet jest szybkie pojawienie się informacji zwrotnych co do poprawności stworzonego przeze mnie kodu. Kiedy piszę test jednostkowy, uruchamiam go i widzę czerwony pasek jUnita, wiem, że mam coś do zrobienia. Po napisaniu implementacji zielony pasek mówi mi, że skończyłem, że ten kawałek kodu działa, i mogę zająć się refaktoryzacją. Dzięki temu pracuję szybko, bo nie muszę uruchamiać aplikacji po każdej zmianie (lub debuggera jeśli po wprowadzeniu 10 zmian uruchamiam aplikację i nie działa poprawnie).&lt;br /&gt;&lt;br /&gt;Patrząc od strony tego doświadczenia, to jest szybkości i pewności tworzenia oprogramowania, dość naturalnym wydaje się chęć, aby tak pracować jak najczęściej. Powiedzmy, że stworzyłem test-first warstwę dostępu do danych dla pewnej funkcjonalności i tak samo warstwę domenową. Teraz chciałbym wiedzieć, czy będą poprawnie współpracowały. Piszę zatem test, który będzie testem integracyjnym, idącym przez te dwie warstwy. Wymusi on na mnie napisanie kodu łączącego ze sobą obiekty z tych dwóch warstw w odpowiedni sposób (&lt;span style="font-style: italic;"&gt;wiring&lt;/span&gt;).&lt;br /&gt;&lt;br /&gt;Idąc dalej: powiedzmy, że właśnie rozpoczynam tworzenie projektu i czeka mnie stworzenie jego początkowej architektury. Mając doświadczenie szybkiej i automatycznej informacji zwrotnej, uzyskiwanej dzięki testom pisanym przed implementacją, mogę chcieć uzyskać taki feedback także i w tym przypadku. W tym celu tworzę właśnie test czegoś, co twórcy książki nazwali &lt;span style="font-style: italic;"&gt;The Walking Skeleton&lt;/span&gt;, czyli szkieletu wszystkich warstw aplikacji. Uruchamiam go i szybko mam informację zwrotną, czy moja infrastruktura jest już gotowa, czy może potrzebuję jeszcze dodatkowego kodu lub konfiguracji, aby aplikacja zaczęła działać.&lt;br /&gt;&lt;br /&gt;Powyższe rozumowanie mam nadzieję pokazuje, dlaczego mógłbym chcieć tworzyć aplikację w sposób opisany przez autorów książki. Tym niemniej, nie znaczy to, że jest to jedyna możliwość w zakresie szeroko rozumianego nurtu TDD. Możliwości tworzenia aplikacji w sposób test-driven jest wiele. Programiści często wypracowują swój styl, a różnice mogą dotyczyć różnych aspektów, takich jak stosowanie testów akceptacyjnych, integracyjnych, używanie mocków, podejście do testowania interfejsu użytkownika.&lt;br /&gt;&lt;br /&gt;Na przykład autorzy wzmiankowanej książki, Steve Freeman i Nat Pryce, stosują podejście do TDD, które można określić jako "Mockist approach", tzn. że mocki są podstawowym narzędziem w tworzeniu testów. Zainteresowanych dokładnym omówieniem różnic pomiędzy tym podejściem a podejściem klasycznym odsyłam do &lt;a href="http://martinfowler.com/articles/mocksArentStubs.html#ClassicalAndMockistTesting"&gt;artykułu Martina Fowlera&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Co do pisania testów przekrojowych (&lt;span style="font-style: italic;"&gt;end-to-end&lt;/span&gt;), to ja nie jestem ich zwolennikiem i zdecydowanie staram się ich unikać. Moim zdaniem testy jednostkowe plus trochę testów integracyjnych daje wystarczająco dużą pewność co do poprawności działania aplikacji. Wadą testów przekrojowych jest przede wszystkim ich powolność. Wyobrażam sobie, że mógłbym ich użyć do stworzenia architektury i być może zainspirowany książką spółki Freeman&amp;amp;Pryce spróbuję, jak taki sposób pracy "smakuje". Dotychczas używałem ich rzadko i raczej jako testy regresji, czy &lt;span style="font-style: italic;"&gt;smoke testy&lt;/span&gt; - zgrubne sprawdzenie, czy nie popsuliśmy aplikacji.&lt;br /&gt;Jeśli już używamy testów end-to-end, trzeba pamiętać o zachowaniu proporcji - zgodnie z &lt;a href="http://benhutchison.wordpress.com/2008/03/20/automated-testing-2-the-test-pyramid/"&gt;piramidą testów&lt;/a&gt; tych przekrojowych powinno być jak najmniej.&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5009385396224025678-2524241619134849653?l=studiopragmatists.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/StudioPragmatists/~4/UqdlZDIbNGA" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://studiopragmatists.blogspot.com/feeds/2524241619134849653/comments/default" title="Komentarze do posta" /><link rel="replies" type="text/html" href="http://studiopragmatists.blogspot.com/2010/03/testy-end-to-end-w-tdd.html#comment-form" title="Komentarze (5)" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5009385396224025678/posts/default/2524241619134849653?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5009385396224025678/posts/default/2524241619134849653?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/StudioPragmatists/~3/UqdlZDIbNGA/testy-end-to-end-w-tdd.html" title="Testy end-to-end w TDD" /><author><name>Krzysztof Jelski</name><uri>http://www.blogger.com/profile/13328086479988072905</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="00293983052099926332" /></author><thr:total>5</thr:total><feedburner:origLink>http://studiopragmatists.blogspot.com/2010/03/testy-end-to-end-w-tdd.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CEAERHw8fyp7ImA9WxBbFkk.&quot;"><id>tag:blogger.com,1999:blog-5009385396224025678.post-8685447450076534235</id><published>2010-03-15T01:00:00.000+01:00</published><updated>2010-03-15T09:45:05.277+01:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-03-15T09:45:05.277+01:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="szkolenie tdd" /><title>Oficjalna strona szkolenia z TDD</title><content type="html">Powoli dopinamy kwestie organizacyjne związane ze szkoleniem Test-Driven Development. Zainteresowanych szczegółami odsyłam do oficjalnej&amp;nbsp;&lt;a href="http://www.pragmatists.pl/pl/szkolenia/tdd/szkolenie.gsp"&gt;strony szkolenia&lt;/a&gt;.&lt;br /&gt;
Polecam szybkie rejestrowanie się, ponieważ ze względu na to, że szkolenie będzie w formie warsztatów, liczba miejsc jest ograniczona!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5009385396224025678-8685447450076534235?l=studiopragmatists.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/StudioPragmatists/~4/2bxetYFw6H8" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://studiopragmatists.blogspot.com/feeds/8685447450076534235/comments/default" title="Komentarze do posta" /><link rel="replies" type="text/html" href="http://studiopragmatists.blogspot.com/2010/03/oficjalna-strona-szkolenia-z-tdd.html#comment-form" title="Komentarze (3)" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5009385396224025678/posts/default/8685447450076534235?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5009385396224025678/posts/default/8685447450076534235?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/StudioPragmatists/~3/2bxetYFw6H8/oficjalna-strona-szkolenia-z-tdd.html" title="Oficjalna strona szkolenia z TDD" /><author><name>Paweł Lipiński</name><uri>http://www.blogger.com/profile/02772675014904905654</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="02130276274143705547" /></author><thr:total>3</thr:total><feedburner:origLink>http://studiopragmatists.blogspot.com/2010/03/oficjalna-strona-szkolenia-z-tdd.html</feedburner:origLink></entry><entry gd:etag="W/&quot;A0IASX89fip7ImA9WxBbEUw.&quot;"><id>tag:blogger.com,1999:blog-5009385396224025678.post-1910632902390299041</id><published>2010-03-04T22:55:00.000+01:00</published><updated>2010-03-09T08:25:48.166+01:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-03-09T08:25:48.166+01:00</app:edited><title>Szkolenie Test-Driven Development</title><content type="html">Szkolenie ze Zwinnego Rozwijania Oprogramowania zostało dobrze przyjęte przez uczestników. Dało nam to dużo radości, satysfakcji, jak również motywacji do tego, aby kontynuować dzielenie się swoją wiedzą i umiejętnościami.&lt;br /&gt;&lt;br /&gt;Tym razem chcemy przekazać wiedzę, która wydaje się nam, zespołowi Pragmatists, bardzo cenna. &lt;span style="font-weight: bold;"&gt;Test-Driven Development&lt;/span&gt;, czyli sposób programowania, w którym zaczynamy od wyrażenia w kodzie naszych intencji, wywiera ogromny pozytywny wpływ na naszą pracę. Bezpieczniejsze, pewniejsze kodowanie. Rzadsze korzystanie z debuggera. Mniej defektów. Lepszy design. Czytelniejszy kod. Krótko mówiąc - zupełnie inaczej niż kiedyś i bardzo przyjemnie.&lt;br /&gt;&lt;br /&gt;Zatem zapraszamy na szkolenie, gdzie podzielimy się naszym doświadczeniem ze stosowania TDD w prawdziwych, komercyjnych projektach. Adresowane jest do programistów i architektów (wymagana znajomość Javy). Możecie liczyć na solidną porcję wiedzy, ale przede wszystkim mnóstwo ćwiczeń, warsztatów, oglądania i pisania kodu. Czas trwania to 3 dni, od 19 do 21 kwietnia 2010. Więcej szczegółów zamieścimy już wkrótce.&lt;br /&gt;&lt;br /&gt;Szkolenie poprowadzi wraz ze mną Michał Margiel, pasjonat Javy, współorganizator Javarsovii, codziennie test-drive'ujący mnóstwo dobrego kodu.&lt;br /&gt;&lt;br /&gt;Poniżej treści, które zamierzamy poruszyć, a większość z nich także przećwiczyć praktycznie podczas warsztatów.&lt;br /&gt;&lt;h4&gt;Wprowadzenie  do TDD&lt;/h4&gt;&lt;ul&gt;&lt;li&gt;czym jest TDD (cykl Red-Green-Refactor, porównanie  do test-last)&lt;/li&gt;&lt;li&gt;korzyści ze stosowania TDD&lt;/li&gt;&lt;li&gt;TDD a  projektowanie (design)&lt;/li&gt;&lt;li&gt;TDD a BDD (Behaviour-Driven Design)&lt;/li&gt;&lt;li&gt;rodzaje  i poziomy testów: jednostkowe, integracyjne, akceptacyjne itp.&lt;/li&gt;&lt;/ul&gt;&lt;h4&gt;Mechanika  TDD&lt;/h4&gt;&lt;ul&gt;&lt;li&gt;wprowadzenie do JUnit&lt;/li&gt;&lt;li&gt;nazewnictwo testów&lt;/li&gt;&lt;li&gt;wsparcie  przez IDE (integracja z JUnit, automatyczne refaktoryzacje, skróty  klawiaturowe)&lt;/li&gt;&lt;li&gt;dyscyplina i rytm&lt;/li&gt;&lt;li&gt;mocki (Mock Objects)&lt;/li&gt;&lt;ul&gt;&lt;li&gt;tworzenie  ręczne&lt;/li&gt;&lt;li&gt;frameworki (szczególny nacisk na Mockito)&lt;/li&gt;&lt;li&gt;inne  rodzaje zaślepek (Test Doubles)&lt;/li&gt;&lt;/ul&gt;&lt;/ul&gt;&lt;h4&gt;Projektowanie  obiektowe&lt;/h4&gt;&lt;ul&gt;&lt;li&gt;projektowanie pod kątem testów  (Testability)&lt;/li&gt;&lt;li&gt;zasady wspierające dobry design (SOLID principles,  Inversion of Control/Dependency Injection, powiązania, spójność)&lt;/li&gt;&lt;/ul&gt;&lt;h4&gt;Pokonywanie  przeszkód&lt;/h4&gt;&lt;ul&gt;&lt;li&gt;tworzenie test-driven warstwy dostępu do danych &lt;/li&gt;&lt;li&gt;tworzenie  test-driven interfejsu użytkownika (GUI)&lt;/li&gt;&lt;/ul&gt;&lt;h4&gt;Praca z  odziedziczonym kodem (Legacy Code)&lt;/h4&gt;&lt;ul&gt;&lt;li&gt;jak pracować bezpiecznie  (testy charakteryzacyjne, zmienne ułatwiające śledzenie)&lt;/li&gt;&lt;li&gt;techniki  rozcinania zależności (dependency breaking)&lt;/li&gt;&lt;li&gt;identyfikacja  punktów newralgicznych&lt;/li&gt;&lt;/ul&gt;&lt;h4&gt;Gdzie iść dalej&lt;/h4&gt;&lt;ul&gt;&lt;li&gt;utrzymywanie  testów&lt;/li&gt;&lt;li&gt;różne podejścia (BDD, ATDD, podejście klasyczne,  podejście mockowe)&lt;/li&gt;&lt;li&gt;programowanie w parach&lt;/li&gt;&lt;li&gt;pokrycie kodu  testami (code coverage)&lt;/li&gt;&lt;li&gt;wprowadzanie TDD w zespole&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5009385396224025678-1910632902390299041?l=studiopragmatists.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/StudioPragmatists/~4/8TE0nUAwSEw" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://studiopragmatists.blogspot.com/feeds/1910632902390299041/comments/default" title="Komentarze do posta" /><link rel="replies" type="text/html" href="http://studiopragmatists.blogspot.com/2010/03/szkolenie-test-driven-development.html#comment-form" title="Komentarze (0)" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5009385396224025678/posts/default/1910632902390299041?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5009385396224025678/posts/default/1910632902390299041?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/StudioPragmatists/~3/8TE0nUAwSEw/szkolenie-test-driven-development.html" title="Szkolenie Test-Driven Development" /><author><name>Krzysztof Jelski</name><uri>http://www.blogger.com/profile/13328086479988072905</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="00293983052099926332" /></author><thr:total>0</thr:total><feedburner:origLink>http://studiopragmatists.blogspot.com/2010/03/szkolenie-test-driven-development.html</feedburner:origLink></entry><entry gd:etag="W/&quot;C0UBRXg9fip7ImA9WxBRGEU.&quot;"><id>tag:blogger.com,1999:blog-5009385396224025678.post-7403488478640003903</id><published>2010-01-07T17:20:00.000+01:00</published><updated>2010-01-07T17:20:54.666+01:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-01-07T17:20:54.666+01:00</app:edited><title>Nie każdy chce pracować w zwinnym zespole?</title><content type="html">&amp;nbsp;W komentarzu do mojego posta o zwinnym szkoleniu Jacek Zawłocki napisał:&lt;br /&gt;
&lt;blockquote&gt;&lt;i&gt;Ja w swojej naiwności sądziłem że wszyscy programiści pragną być członkiem zespołu Agile, tylko kierownictwo uważa Agile i programowanie w parach za fanaberię, hamując w rezultacie zapędy programistów. Jednak tak nie jest, co mnie bardzo zaskoczyło. Sami programiści nie wierzą, że to działa.&amp;nbsp;&lt;/i&gt;&lt;br /&gt;
&lt;/blockquote&gt;I faktycznie Jacek ma rację. Z czego to wynika?&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;1. Świadomość czym jest zwinność&lt;/b&gt;&lt;br /&gt;
Wielu programistów "naturalnie" skupia się na procesach, procedurach, praktykach. Codziennie nimi właśnie się zajmujemy. Je tworzymy w oprogramowaniu. Do ich stosowania jesteśmy przyzwyczajeni. Trudno jest więc przyjąć, że są one tylko środkiem a nie celem.&lt;br /&gt;
W zwinnych metodykach podkreśla się adaptacyjność - celem jest tworzenie wartości i dostarczanie jej często. Jak to robimy jest zależne od zespołu i warunków. Nie ma więc raczej uniwersalnych praktyk. Nawet iteracyjność, która jest często utożsamiana ze zwinnością, nie jest zawsze nieodzowna (np. Kanban nie jest iteracyjny - skupia się na równomiernym przepływie).&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;2. U nas się nie da ...&lt;/b&gt;&lt;br /&gt;
Wielu programistów dochodzi do wniosku, że nie mogą pracować zwinnie / że ich zespół nie może być zwinny, bo ich manager nigdy nie zgodzi się na ... (tu można wstawić cokolwiek: TDD, refaktoryzację, programowanie w parach, itp.) I to może być nawet prawda. Ale zwinny zespół to ten, który dostosowuje się do warunków. Jeśli nie da się robić TDD ("od testów mamy dział testów") to tego nie róbcie. Starajcie się inaczej dbać o jakość. Skupcie się na tym co można, co się da robić, a nie na tym czego się nie da. Jeśli można spotkać się z klientem/użytkownikiem co jakiś czas by pokazać mu aplikację i otrzymać komentarze - róbcie to. Jeśli można codziennie rano poświęcić 5min na ustalenie kto co zrobił i co planuje na dziś, warto to wprowadzić.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;3. Przezroczystość&lt;/b&gt;&lt;br /&gt;
Praca w zwinnym zespole wymaga założenia, że mamy wspólny cel, i że realizujemy go razem. Dlatego chcemy wprowadzić maksymalną przezroczystość procesu i pracy, by każdy zawsze wiedział "gdzie jest" i by można było szybciej i odpowiedzialniej reagować na to co się dzieje w projekcie.&lt;br /&gt;
Nie każdy jednak chce by mu inni zaglądali w kod, by inni dokładnie wiedzieli co robi. Szczególnie osoby przyzwyczajone do pracy ściśle indywidualnej mogą mieć opory żeby poświęcić część swojej prywatności zawodowej (skądinąd&amp;nbsp;czy to czasem nie jest oksymoron?)&lt;br /&gt;
W zwinnych zespołach zakładamy, że nic w pracy nie jest indywidualne - ani kod, ani czas pracy&amp;nbsp;nie jest własnością programisty. Odpowiedzialność i uczciwość zawodowa, ale również prędkość pracy i zaufanie w zespole i klienta względem zespołu wymagają jawności.&lt;br /&gt;
&lt;br /&gt;
Wierzę, że jeśli przyjmie się takie proaktywne, pozytywne nastawienie dużo łatwiej o zwinność.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5009385396224025678-7403488478640003903?l=studiopragmatists.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/StudioPragmatists/~4/zoZNnuysgKA" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://studiopragmatists.blogspot.com/feeds/7403488478640003903/comments/default" title="Komentarze do posta" /><link rel="replies" type="text/html" href="http://studiopragmatists.blogspot.com/2010/01/nie-kazdy-chce-pracowac-w-zwinnym.html#comment-form" title="Komentarze (1)" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5009385396224025678/posts/default/7403488478640003903?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5009385396224025678/posts/default/7403488478640003903?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/StudioPragmatists/~3/zoZNnuysgKA/nie-kazdy-chce-pracowac-w-zwinnym.html" title="Nie każdy chce pracować w zwinnym zespole?" /><author><name>Paweł Lipiński</name><uri>http://www.blogger.com/profile/02772675014904905654</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="02130276274143705547" /></author><thr:total>1</thr:total><feedburner:origLink>http://studiopragmatists.blogspot.com/2010/01/nie-kazdy-chce-pracowac-w-zwinnym.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DEEBSXs8eSp7ImA9WxFTGEw.&quot;"><id>tag:blogger.com,1999:blog-5009385396224025678.post-7876994522373926661</id><published>2009-12-09T19:10:00.001+01:00</published><updated>2010-04-09T14:37:38.571+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-04-09T14:37:38.571+02:00</app:edited><title>Po Zwinnym Szkoleniu</title><content type="html">W ostatni piątek i sobotę prowadziliśmy &lt;a href="http://pragmatists.pl/szkolenie/Szkolenie%20-%20Zwinne%20Rozwijanie%20Oprogramowania.pdf"&gt;szkolenie ze Zwinnego Rozwijania Oprogramowania&lt;/a&gt;.&lt;br /&gt;
Cel był dość prosty - przekazać w te dwa dni jak najwięcej wiedzy dotyczącej zwinnych metodyk i&amp;nbsp; myślenia/filozofii z nimi związanej. Najważniejsze dla nas było zrobienie tego w taki sposób, który pozwoli kursantom zacząć wdrażać nowopozyskaną wiedzę już pierwszego dnia po szkoleniu. Opierało się więc ono w dużej mierze na warsztatach i ćwiczeniach.&lt;br /&gt;
&lt;br /&gt;
Agenda szkolenia była następująca:&lt;br /&gt;
1) &lt;b&gt;Wprowadzenie do Zwinnego Rozwijania Oprogramowania&lt;/b&gt;&lt;br /&gt;
Bazując na manifeście przekazaliśmy wszystkie główne myśli i zasady związane ze zwinnym myśleniem. Porównaliśmy zwinne metodyki z tradycyjnym "inżynierskim" podejściem. W ramach warsztatów pokazaliśmy, że zwinne metodyki dotykają wszystkich głównych problemów związanych z prowadzeniem i realizacją projektów programistycznych.&lt;br /&gt;
2) &lt;b&gt;Scrum &amp;amp; eXtreme Programming&lt;/b&gt;&lt;br /&gt;
W tej części zaprezentowaliśmy dwie różne, ale zwykle używane komplementarnie zwinne metodyki: Scrum i XP. Staraliśmy się pokazać jak wypełniają one zasady zawarte w &lt;a href="http://agilemanifesto.org/"&gt;Manifeście&lt;/a&gt;. Przy okazji w ramach warsztatów z pomocą Scrum'a pisaliśmy wspólnie poezję :)&lt;br /&gt;
3) &lt;b&gt;Zwinne praktyki programistyczne&lt;/b&gt;&lt;br /&gt;
Ta część przybliżyła założenia co do pisania oprogramowania związane z XP (TDD, programowanie w parach, Continous Integration), co to znaczy pisać &lt;a href="http://blog.objectmentor.com/articles/2008/04/08/clean-code-whew"&gt;Czysty Kod&lt;/a&gt; oraz opisała ideę &lt;a href="http://manifesto.softwarecraftsmanship.org/"&gt;Software Craftsmanship&lt;/a&gt;.&lt;br /&gt;
Krótki wykład zakończył się prezentacją połączenia TDD z programowaniem w parach (tzw. TDD ping-pong).&lt;br /&gt;
4)&lt;b&gt; Zwinne Szacowanie i Planowanie&lt;/b&gt;&lt;br /&gt;
W ramach tej części pogłębiliśmy temat szacowania i planowania bazującego na Scenariuszach (User Stories), pokazaliśmy jak robić to w grupie (Planning Poker) oraz w jaki sposób wyceniać i planować iteracje i wydania. Nawiązaliśmy do tego jak mogą wyglądać umowy w zwinnych projektach i jak działać zwinnie w przypadku projektów fixed-time &amp;amp; fixed-scope. Pokazaliśmy jak liczyć prędkość zespołu i do czego można ją wykorzystać.&lt;br /&gt;
Zdobytą wiedzę wykorzystaliśmy do szacowania codziennych czynności takich jak pisanie smsów, czytanie książek czy wieszanie firanek :)&lt;br /&gt;
5) &lt;b&gt;Zwinne zespoły&lt;/b&gt;&lt;br /&gt;
W tej części opowiadaliśmy o tym czym zwinne zespoły różnią się od zwykłych, co wpływa na efektywność pracy zespołu, co można zrobić, by zespół poprawiał jakość tak swoją jak i tworzonych przez siebie produktów. Pogłębiliśmy temat retrospektywy jako mechanizmu ułatwiającego zespołowi unaocznianie problemów i ciągłe poprawianie się. Na koniec przeprowadziliśmy warsztaty z prowadzenia retrospekcji.&lt;br /&gt;
6) &lt;b&gt;XP Lego Game&lt;/b&gt;&lt;br /&gt;
Jako podsumowanie praktycznej części szkolenia zrealizowaliśmy kompletny projekt z wykorzystaniem planowania, szacowania i retrospekcji. Właściciel produktu weryfikował poprawność wykonania, wymagał refaktorowania części i dotrzymywania terminów. W ten sposób zrealizowaliśmy park z klocków lego:&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;br /&gt;
&lt;/div&gt;&lt;br /&gt;
&lt;a href="http://blog.pragmatists.pl/uploaded_images/collage-705000-740802.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="200" src="http://blog.pragmatists.pl/uploaded_images/collage-705000-740799.jpg" width="320" /&gt;&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
7)&lt;b&gt; Transformacja zespołów i organizacji&lt;/b&gt;&lt;br /&gt;
Ta, najbardziej teoretyczna część, dotyczyła wprowadzania zwinnych metodyk w istniejących zespołach, transformacji organizacji i sposobów pracy zwinnych zespołów z niezwinnymi działami. Dotknęliśmy zagadnień przekazywania odpowiedzialności i uprawnień związanych z projektem zespołom,&amp;nbsp; roli menagera, lidera i coach'a w zwinnych zespołach.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Retrospektywa&lt;/b&gt;&lt;br /&gt;
Na koniec szkolenia poprosiliśmy kursantów o wypełnienie ankiety, na podstawie której wyciągnęliśmy następujące wnioski (z powtarzających się komentarzy i ocen każdej z części oddzielnie):&lt;br /&gt;
&lt;blockquote&gt;Co się udało:&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;szkolenie bardzo się podobało - 98% uczestników uważało je za bardzo dobre&lt;/li&gt;
&lt;li&gt;dwóch prowadzących o różnych temperamentach i punktach widzenia poszerzało perspektywę (zostaliśmy nazwani "dobry i zły glina" ;-))&lt;/li&gt;
&lt;li&gt;wprowadzenie praktycznych warsztatów było dobrym pomysłem&lt;/li&gt;
&lt;li&gt;projekt parku z LEGO był hitem :)&lt;/li&gt;
&lt;li&gt;zrównoważyliśmy teorię z praktyką - zadowolone były zarówno osoby chcące poznać podstawy teoretyczne (już pracujące w Scrumie), jak i osoby bardziej oczekujące praktycznych informacji (zadowolenie z warsztatów i prezentacji TDD)&lt;/li&gt;
&lt;li&gt;wszystko było prawie idealnie na czas&lt;/li&gt;
&lt;/ul&gt;&lt;/blockquote&gt;&lt;blockquote&gt;Co warto poprawić na następny raz:&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;lokalizacja bliżej centrum&lt;/li&gt;
&lt;li&gt;salka szkoleniowa mogłaby być trochę większa&lt;/li&gt;
&lt;li&gt;prezentacja TDD mogłaby być nie na koniec dnia, byłaby okazja podyskutować&lt;/li&gt;
&lt;li&gt;prezentacja TDD powinna skupić się albo na refaktoryzacji, albo na &lt;i&gt;drive'owaniu&lt;/i&gt; nowego kodu żeby była czytelniejsza&lt;/li&gt;
&lt;li&gt;część poświęcona transformacji powinna mieć przykłady z życia wzięte&lt;/li&gt;
&lt;/ul&gt;&lt;/blockquote&gt;&lt;br /&gt;
Na początku przyszłego roku planujemy otwarte szkolenie-warsztaty z TDD, na które już teraz zapraszamy.&lt;br /&gt;
&lt;br /&gt;
Jesteśmy gotowi powtórzyć Zwinne Szkolenie dla konkretnych zespołów (jako szkolenie zamknięte), oraz zachęcamy do zgłaszania swojego zainteresowania udziałem w kolejnym takim szkoleniu otwartym, które pewnie odbędzie się też w pierwszej części przyszłego roku.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5009385396224025678-7876994522373926661?l=studiopragmatists.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/StudioPragmatists/~4/IvmBigWDZv8" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://studiopragmatists.blogspot.com/feeds/7876994522373926661/comments/default" title="Komentarze do posta" /><link rel="replies" type="text/html" href="http://studiopragmatists.blogspot.com/2009/12/po-zwinnym-szkoleniu.html#comment-form" title="Komentarze (5)" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5009385396224025678/posts/default/7876994522373926661?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5009385396224025678/posts/default/7876994522373926661?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/StudioPragmatists/~3/IvmBigWDZv8/po-zwinnym-szkoleniu.html" title="Po Zwinnym Szkoleniu" /><author><name>Paweł Lipiński</name><uri>http://www.blogger.com/profile/02772675014904905654</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="02130276274143705547" /></author><thr:total>5</thr:total><feedburner:origLink>http://studiopragmatists.blogspot.com/2009/12/po-zwinnym-szkoleniu.html</feedburner:origLink></entry><entry gd:etag="W/&quot;AkICQX04fCp7ImA9WxNbE0o.&quot;"><id>tag:blogger.com,1999:blog-5009385396224025678.post-2328649521097950699</id><published>2009-11-16T12:52:00.000+01:00</published><updated>2009-11-16T14:36:00.334+01:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-11-16T14:36:00.334+01:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="rekrutacja" /><title>Rekrutacja</title><content type="html">Rozpoczynamy oficjalną rekrutację do naszego zespołu. Szukamy ludzi, dla których tworzenie oprogramowania to pasja. Takich, którzy już umieją wiele ale chcą nauczyć się jeszcze więcej. Ludzi, którzy chcą i umieją pisać czysty kod. Którzy dbają o swoją pracę i zespół z którym pracują.&lt;br /&gt;&lt;br /&gt;Oczekiwania:&lt;br /&gt;- doskonała znajomość Java/JEE&lt;br /&gt;- świetna znajomość kilku z javowych frameworków i bibliotek  wykorzystywanych w aplikacjach klasy enterprise (takich jak Hibernate, Spring)&lt;br /&gt;- umiejętność tworzenia aplikacji z grubym klientem (Swing)&lt;br /&gt;&lt;br /&gt;Atuty dodatkowe:&lt;br /&gt;- doświadczenie pracy w zwinnych metodykach&lt;br /&gt;- TDD, refaktoryzacja, mockowanie, wzorce projektowe&lt;br /&gt;- doświadczenie w tworzeniu aplikacji bazujących na NetBeans Platform&lt;br /&gt;&lt;br /&gt;Gwarantujemy:&lt;br /&gt;- pracę z bardzo dobrymi programistami w zgranym zespole&lt;br /&gt;- pracę w zwinnych metodykach (eXtreme Programming / Scrum)&lt;br /&gt;- pracę z kodem wysokiej jakości&lt;br /&gt;Ponadto jesteśmy pewni, że już po paru tygodniach pracy z nami staniesz się jeszcze lepszym programistą i nauczysz się bardzo dużo.&lt;br /&gt;&lt;br /&gt;Zainteresowanych prosimy o przesłanie swojego CV wraz z krótkim opisem dlaczego chciał(a)byś u nas pracować i dlaczego właśnie Ciebie mamy zaprosić do naszego zespołu na adres &lt;a href="mailto:contact@pragmatists.pl"&gt;contact@pragmatists.pl&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5009385396224025678-2328649521097950699?l=studiopragmatists.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/StudioPragmatists/~4/gxGDrHKRiYI" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://studiopragmatists.blogspot.com/feeds/2328649521097950699/comments/default" title="Komentarze do posta" /><link rel="replies" type="text/html" href="http://studiopragmatists.blogspot.com/2009/11/rekrutacja.html#comment-form" title="Komentarze (1)" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5009385396224025678/posts/default/2328649521097950699?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5009385396224025678/posts/default/2328649521097950699?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/StudioPragmatists/~3/gxGDrHKRiYI/rekrutacja.html" title="Rekrutacja" /><author><name>Pragmatists</name><uri>http://www.blogger.com/profile/02774380699386691980</uri><email>contact@pragmatists.pl</email><gd:extendedProperty name="OpenSocialUserId" value="02426067448878824628" /></author><thr:total>1</thr:total><feedburner:origLink>http://studiopragmatists.blogspot.com/2009/11/rekrutacja.html</feedburner:origLink></entry><entry gd:etag="W/&quot;AkUARH47eCp7ImA9WxNbEUk.&quot;"><id>tag:blogger.com,1999:blog-5009385396224025678.post-837698916539843640</id><published>2009-11-13T22:37:00.000+01:00</published><updated>2009-11-13T22:37:25.000+01:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-11-13T22:37:25.000+01:00</app:edited><title>Zwinne szkolenie</title><content type="html">W dniach 4-5 grudnia organizujemy szkolenie: Zwinne Rozwijanie Oprogramowania (ang. Agile Software Development :)). Prowadzić będę je ja wraz z Krzyśkiem Jelskim.&lt;br /&gt;
Wszelkie szczegóły dotyczące szkolenia można uzyskać pod adresem &lt;a href="http://pragmatists.pl/szkolenie"&gt;http://pragmatists.pl/szkolenie&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
W tej chwili mamy zarezerwowaną już ponad połowę miejsc.&lt;br /&gt;
Jako, że do końca rejestracji pozostał już tylko tydzień, zachęcamy do zgłaszania się!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5009385396224025678-837698916539843640?l=studiopragmatists.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/StudioPragmatists/~4/KtQDttHEj74" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://studiopragmatists.blogspot.com/feeds/837698916539843640/comments/default" title="Komentarze do posta" /><link rel="replies" type="text/html" href="http://studiopragmatists.blogspot.com/2009/11/zwinne-szkolenie.html#comment-form" title="Komentarze (0)" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5009385396224025678/posts/default/837698916539843640?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5009385396224025678/posts/default/837698916539843640?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/StudioPragmatists/~3/KtQDttHEj74/zwinne-szkolenie.html" title="Zwinne szkolenie" /><author><name>Paweł Lipiński</name><uri>http://www.blogger.com/profile/02772675014904905654</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="02130276274143705547" /></author><thr:total>0</thr:total><feedburner:origLink>http://studiopragmatists.blogspot.com/2009/11/zwinne-szkolenie.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DUcDSHo4cSp7ImA9WxNUGEU.&quot;"><id>tag:blogger.com,1999:blog-5009385396224025678.post-2603586625759054157</id><published>2009-11-10T22:04:00.001+01:00</published><updated>2009-11-10T22:04:39.439+01:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-11-10T22:04:39.439+01:00</app:edited><title>Blog firmowy - Początek</title><content type="html">Niniejszym rozpoczynamy firmowy blog. Będzie czasem techniczny, czasem  dotyczący tego, co robimy, co organizujemy, do czego dążymy.&lt;br /&gt;Zapraszamy!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5009385396224025678-2603586625759054157?l=studiopragmatists.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/StudioPragmatists/~4/c8gihSIAwzI" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://studiopragmatists.blogspot.com/feeds/2603586625759054157/comments/default" title="Komentarze do posta" /><link rel="replies" type="text/html" href="http://studiopragmatists.blogspot.com/2009/11/blog-firmowy-poczatek.html#comment-form" title="Komentarze (0)" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5009385396224025678/posts/default/2603586625759054157?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5009385396224025678/posts/default/2603586625759054157?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/StudioPragmatists/~3/c8gihSIAwzI/blog-firmowy-poczatek.html" title="Blog firmowy - Początek" /><author><name>Pragmatists</name><uri>http://www.blogger.com/profile/02774380699386691980</uri><email>contact@pragmatists.pl</email><gd:extendedProperty name="OpenSocialUserId" value="02426067448878824628" /></author><thr:total>0</thr:total><feedburner:origLink>http://studiopragmatists.blogspot.com/2009/11/blog-firmowy-poczatek.html</feedburner:origLink></entry></feed>
