Agile http://blogs.ugidotnet.org/bmatte/category/Agile.aspx Agile it-IT Matteo Baglini Subtext Version 2.6.0.0 WPF Functional Testing With White http://blogs.ugidotnet.org/bmatte/archive/2008/12/18/wpf-functional-testing-with-white.aspx <p>(@ <a href="http://blogs.ugidotnet.org/dsantarelli" target="_blank">Dario</a>: accetto la sfida!!! <img alt="smile_teeth" src="http://spaces.live.com/rte/emoticons/smile_teeth.gif" /> )</p> <p>Nel mio <a href="http://blogs.ugidotnet.org/bmatte/archive/2008/12/16/wpf-ui-test-framework.aspx" target="_blank">ultimo post</a> ho annunciato il rilascio di un framework per effettuare test funzionali per applicazioni desktop in maniera del tutto automatica. <a href="http://blogs.ugidotnet.org/dsantarelli" target="_blank">Dario</a> ha accettato la mia <a href="http://blogs.ugidotnet.org/bmatte/archive/2008/12/16/wpf-ui-test-framework.aspx#1405390" target="_blank">richiesta</a> rispondendo con un bel <a href="http://blogs.ugidotnet.org/dsantarelli/archive/2008/12/17/testapi-e-input-injection.aspx" target="_blank">post su TestApi e Input Injection</a>. Tutto ciò mi ha messo voglia di provare a rifare il solito test da lui proposto, però utilizzando <a href="http://www.codeplex.com/white" target="_blank">white</a>, così possiamo confrontare i due framework. Cominciamo!!</p> <p>Prima di tutto lo screenshot della mia applicazione, non è proprio identica a quella di Dario, ma va benissimo!</p> <p><a href="http://blogs.ugidotnet.org/images/blogs_ugidotnet_org/bmatte/WindowsLiveWriter/UIFunctionalTestingWithWhite_12787/ui%20test_2.png" target="_blank"><img title="ui test" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="232" alt="ui test" src="http://blogs.ugidotnet.org/images/blogs_ugidotnet_org/bmatte/WindowsLiveWriter/UIFunctionalTestingWithWhite_12787/ui%20test_thumb.png" width="384" border="0" /></a> </p> <p>Come sappiamo, lo scopo del test è quello di:</p> <ol> <li>Lanciare l'applicazione </li> <li>Selezione della modalità di filtraggio (ovvero selezione di un item della ComboBox) </li> <li>Inserimento della filterExpression (typing nella TextBox) </li> <li>Click del Button di ricerca </li> <li>Valutazione dei risultati nella ListBox </li> </ol> <p>Niente di più facile!! <img alt="smile_wink" src="http://spaces.live.com/rte/emoticons/smile_wink.gif" /> Come per le <a href="http://www.codeplex.com/TestApi" target="_blank">TestApi</a>, per prima cosa nello XAML dobbiamo impostare per ogni elemento interessato l’Attached Properties <a href="http://msdn.microsoft.com/en-us/library/system.windows.automation.automationproperties.automationid.aspx" target="_blank">AutomationProperties.AutomationId</a>, successivamente aggiungiamo un progetto di tipo Class Library per i test. Aggiungiamo come reference al progetto le librerie Core.dll, White.NUnit.dll e nunit.framework.dll, quest’ultima libreria viene fornita insieme ai binari di <a href="http://www.codeplex.com/white" target="_blank">white</a> però in realtà fa parte del framework <a href="http://www.nunit.org/" target="_blank">NUnit</a>. Adesso non ci resta che aggiungere una nuava classe e scrivere il metodo di test come mostrato nel seguente snippet:</p> <div class="csharpcode"> <pre class="alt"><span class="lnum"> 1: </span>[TestFixture]</pre> <pre><span class="lnum"> 2: </span><span class="kwrd">public</span> <span class="kwrd">class</span> MainWindowTests</pre> <pre class="alt"><span class="lnum"> 3: </span>{</pre> <pre><span class="lnum"> 4: </span> <span class="kwrd">private</span> <span class="kwrd">const</span> <span class="kwrd">string</span> path = <span class="str">@"C:\…\WhiteDemoWithWpf.exe"</span>;</pre> <pre class="alt"><span class="lnum"> 5: </span> </pre> <pre><span class="lnum"> 6: </span> [Test]</pre> <pre class="alt"><span class="lnum"> 7: </span> <span class="kwrd">public</span> <span class="kwrd">void</span> When_click_search_should_filter_loaded_data_by_selected_mode_and_typed_expression()</pre> <pre><span class="lnum"> 8: </span> {</pre> <pre class="alt"><span class="lnum"> 9: </span> <span class="kwrd">string</span> productNameToMatch = <span class="str">"Chang"</span>;</pre> <pre><span class="lnum"> 10: </span> </pre> <pre class="alt"><span class="lnum"> 11: </span> <span class="rem">//Esecuzione dell'applicazione</span></pre> <pre><span class="lnum"> 12: </span> var application = Application.Launch(path);</pre> <pre class="alt"><span class="lnum"> 13: </span> </pre> <pre><span class="lnum"> 14: </span> <span class="rem">//Ottengo la Window e verifico che non sia Null</span></pre> <pre class="alt"><span class="lnum"> 15: </span> var window = application.GetWindow(<span class="str">"MainWindow"</span>, InitializeOption.NoCache);</pre> <pre><span class="lnum"> 16: </span> Assert.That(window,Is.Not.Null);</pre> <pre class="alt"><span class="lnum"> 17: </span> </pre> <pre><span class="lnum"> 18: </span> <span class="rem">//Seleziono un elemento della ComboBox</span></pre> <pre class="alt"><span class="lnum"> 19: </span> var lstFilterCriteria = window.Get&lt;ComboBox&gt;(<span class="str">"lstFilterCriteria"</span>);</pre> <pre><span class="lnum"> 20: </span> lstFilterCriteria.Select(3);</pre> <pre class="alt"><span class="lnum"> 21: </span> </pre> <pre><span class="lnum"> 22: </span> <span class="rem">//Scrivo il Product Name per il test</span></pre> <pre class="alt"><span class="lnum"> 23: </span> var txtProductName = window.Get&lt;TextBox&gt;(<span class="str">"txtProductName"</span>);</pre> <pre><span class="lnum"> 24: </span> txtProductName.Text = productNameToMatch;</pre> <pre class="alt"><span class="lnum"> 25: </span> </pre> <pre><span class="lnum"> 26: </span> <span class="rem">//Clicco il Button per effettuare la ricerca</span></pre> <pre class="alt"><span class="lnum"> 27: </span> var btnFilterProducts = window.Get&lt;Button&gt;(<span class="str">"btnFilterProducts"</span>);</pre> <pre><span class="lnum"> 28: </span> btnFilterProducts.Click();</pre> <pre class="alt"><span class="lnum"> 29: </span> </pre> <pre><span class="lnum"> 30: </span> <span class="rem">//Verifico che il numero totale di elementi della lista sia 1</span></pre> <pre class="alt"><span class="lnum"> 31: </span> var listViewProducts = window.Get&lt;ListView&gt;(<span class="str">"listViewProducts"</span>);</pre> <pre><span class="lnum"> 32: </span> Assert.That(listViewProducts.Rows.Count, Is.EqualTo(1));</pre> <pre class="alt"><span class="lnum"> 33: </span> </pre> <pre><span class="lnum"> 34: </span> <span class="rem">//Verifico il contenuto della cella contenente </span></pre> <pre class="alt"><span class="lnum"> 35: </span> <span class="rem">//il risultato della ricerca con il Product Name per il test</span></pre> <pre><span class="lnum"> 36: </span> var cell = listViewProducts.Rows[0].Cells[0];</pre> <pre class="alt"><span class="lnum"> 37: </span> Assert.That(cell.Text, Is.EqualTo(productNameToMatch));</pre> <pre><span class="lnum"> 38: </span> </pre> <pre class="alt"><span class="lnum"> 39: </span> <span class="rem">//Chiudo l'applicazione</span></pre> <pre><span class="lnum"> 40: </span> application.Kill();</pre> <pre class="alt"><span class="lnum"> 41: </span> }</pre> <pre><span class="lnum"> 42: </span>}</pre> </div> <style type="text/css"><![CDATA[ .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }]]></style> <p>I commenti descrivono chiaramente la logica del metodo. Lanciamo il test (sempre con le mani dietro alla testa <img alt="smile_wink" src="http://spaces.live.com/rte/emoticons/smile_wink.gif" />) e godiamoci il risultato:</p> <p><a href="http://blogs.ugidotnet.org/images/blogs_ugidotnet_org/bmatte/WindowsLiveWriter/UIFunctionalTestingWithWhite_12787/CropperCapture%5B1%5D.png" target="_blank"><img title="CropperCapture[1]" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="196" alt="CropperCapture[1]" src="http://blogs.ugidotnet.org/images/blogs_ugidotnet_org/bmatte/WindowsLiveWriter/UIFunctionalTestingWithWhite_12787/CropperCapture%5B1%5D_thumb.png" width="862" border="0" /></a> </p> <p /> <p>Le prime tre righe sono “problemi” di <a href="http://www.codeplex.com/white/Wiki/View.aspx?title=Configuration&amp;referringTitle=Home" target="_blank">confgurazione</a> non essenziali per questa demo. </p> <p>Confrontando il solito metodo di test scritto con i due framework devo dire che <a href="http://www.codeplex.com/white" target="_blank">white</a> al momento offre un modello di pilotaggio della UI molto più ricco e semplice da utilizzare. Atro aspetto interessante è che <a href="http://www.codeplex.com/white" target="_blank">white</a> permette di testare anche applicazioni Java scritte in SWT!!</p> <p>Devo ammettere che vedere l’applicazione vivere di vita propria fa un certo effetto!!! <img alt="smile_teeth" src="http://spaces.live.com/rte/emoticons/smile_teeth.gif" /></p> <div class="wlWriterEditableSmartContent" id="scid:0767317B-992E-4b12-91E0-4F059A8CECA8:79cbd1a6-410f-4088-86e4-0e83dd6c8349" style="padding-right: 0px; display: inline; padding-left: 0px; float: none; padding-bottom: 0px; margin: 0px; padding-top: 0px">Technorati Tag: <a href="http://technorati.com/tags/WPF" rel="tag">WPF</a>,<a href="http://technorati.com/tags/Windows+Presentation+Foundation" rel="tag">Windows Presentation Foundation</a>,<a href="http://technorati.com/tags/FunctionalTest" rel="tag">FunctionalTest</a>,<a href="http://technorati.com/tags/White" rel="tag">White</a></div><img src="http://blogs.ugidotnet.org/bmatte/aggbug/94978.aspx" width="1" height="1" /> Matteo Baglini http://blogs.ugidotnet.org/bmatte/archive/2008/12/18/wpf-functional-testing-with-white.aspx Thu, 18 Dec 2008 23:23:02 GMT http://blogs.ugidotnet.org/bmatte/archive/2008/12/18/wpf-functional-testing-with-white.aspx#feedback 2 http://blogs.ugidotnet.org/bmatte/comments/commentRss/94978.aspx http://blogs.ugidotnet.org/bmatte/services/trackbacks/94978.aspx WPF UI Test Framework http://blogs.ugidotnet.org/bmatte/archive/2008/12/16/wpf-ui-test-framework.aspx <p><a href="http://blogs.msdn.com/ivo_manolov/archive/2008/12/06/9181396.aspx" target="_blank">Ivo Manolov annumcia</a> sul proprio blog il <a href="http://www.codeplex.com/TestApi" target="_blank">rilascio di un tool open source</a> per automatizzare il test della UI sia essa in WPF pittosto che WinForm. Sempre sul suo blog potete leggere il <a href="http://blogs.msdn.com/ivo_manolov/archive/2008/12/15/9223397.aspx" target="_blank">primo post guida</a> del tool. Da questi post ho scoperto che esiste un <a href="http://blogs.msdn.com/wpftesting/" target="_blank">blog su MSDN dedicato al testing di WPF</a>. </p> <p>Esisteva già un tool open surce simile si chiama <a href="http://www.codeplex.com/white" target="_blank">white ed anche questo è hostato su CodePlex</a>.</p> <div class="wlWriterEditableSmartContent" id="scid:0767317B-992E-4b12-91E0-4F059A8CECA8:f3edd854-9fff-40ea-bb0c-b788235ffd4d" style="padding-right: 0px; display: inline; padding-left: 0px; float: none; padding-bottom: 0px; margin: 0px; padding-top: 0px">Technorati Tag: <a href="http://technorati.com/tags/UnitTest" rel="tag">UnitTest</a>,<a href="http://technorati.com/tags/UI+Test" rel="tag">UI Test</a></div><img src="http://blogs.ugidotnet.org/bmatte/aggbug/94957.aspx" width="1" height="1" /> Matteo Baglini http://blogs.ugidotnet.org/bmatte/archive/2008/12/16/wpf-ui-test-framework.aspx Tue, 16 Dec 2008 17:02:24 GMT http://blogs.ugidotnet.org/bmatte/archive/2008/12/16/wpf-ui-test-framework.aspx#feedback 3 http://blogs.ugidotnet.org/bmatte/comments/commentRss/94957.aspx http://blogs.ugidotnet.org/bmatte/services/trackbacks/94957.aspx Google C++ Mocking Framework http://blogs.ugidotnet.org/bmatte/archive/2008/12/16/google-c-mocking-framework.aspx <p>Ricordate il <a href="http://blogs.ugidotnet.org/bmatte/archive/2008/07/16/google-c-testing-framework.aspx" target="_blank">Google C++ Test Framework</a>? Bene! Adesso il Team di Google ha rilasciato anche un framework di <a href="http://googletesting.blogspot.com/2008/12/announcing-google-c-mocking-framework.html" target="_blank">Mocking per C++</a>. </p> <p>Vediamo se mi sarà utilie nella solita applicazione C++ che ho menzionato nel post passato.</p> <div class="wlWriterEditableSmartContent" id="scid:0767317B-992E-4b12-91E0-4F059A8CECA8:5ffd4db2-4d85-4c2d-88f2-fdf326ecf930" style="padding-right: 0px; display: inline; padding-left: 0px; float: none; padding-bottom: 0px; margin: 0px; padding-top: 0px">Technorati Tag: <a href="http://technorati.com/tags/Mocks" rel="tag">Mocks</a>,<a href="http://technorati.com/tags/UnitTest" rel="tag">UnitTest</a>,<a href="http://technorati.com/tags/TDD" rel="tag">TDD</a></div><img src="http://blogs.ugidotnet.org/bmatte/aggbug/94955.aspx" width="1" height="1" /> Matteo Baglini http://blogs.ugidotnet.org/bmatte/archive/2008/12/16/google-c-mocking-framework.aspx Tue, 16 Dec 2008 16:45:45 GMT http://blogs.ugidotnet.org/bmatte/archive/2008/12/16/google-c-mocking-framework.aspx#feedback 1 http://blogs.ugidotnet.org/bmatte/comments/commentRss/94955.aspx http://blogs.ugidotnet.org/bmatte/services/trackbacks/94955.aspx Google Testing Blog - Clean Code Talks http://blogs.ugidotnet.org/bmatte/archive/2008/12/02/google-testing-blog-clean-code-talks.aspx <p>Chiunque di voi scriva test unitari per il proprio codice (perché esiste ancora qualcuno che non lo fa?? <img alt="smile_wink" src="http://spaces.live.com/rte/emoticons/smile_wink.gif" />) consiglio questi brevi video di Google:</p> <h4><a href="http://googletesting.blogspot.com/2008/11/clean-code-talks-unit-testing.html" target="_blank">Clean Code Talks - Unit Testing</a></h4> <h4><a href="http://googletesting.blogspot.com/2008/11/clean-code-talks-dependency-injection.html" target="_blank">Clean Code Talks - Dependency Injection</a></h4> <h4><a href="http://googletesting.blogspot.com/2008/11/clean-code-talks-global-state-and.html" target="_blank">Clean Code Talks - Global State and Singletons</a></h4> <div class="wlWriterEditableSmartContent" id="scid:0767317B-992E-4b12-91E0-4F059A8CECA8:d9aa398a-7f96-4e78-94c7-59f3d592192f" style="padding-right: 0px; display: inline; padding-left: 0px; float: none; padding-bottom: 0px; margin: 0px; padding-top: 0px">Technorati Tag: <a href="http://technorati.com/tags/UnitTest" rel="tag">UnitTest</a>,<a href="http://technorati.com/tags/DependencyInjection" rel="tag">DependencyInjection</a>,<a href="http://technorati.com/tags/Pattern" rel="tag">Pattern</a></div><img src="http://blogs.ugidotnet.org/bmatte/aggbug/94828.aspx" width="1" height="1" /> Matteo Baglini http://blogs.ugidotnet.org/bmatte/archive/2008/12/02/google-testing-blog-clean-code-talks.aspx Tue, 02 Dec 2008 17:17:13 GMT http://blogs.ugidotnet.org/bmatte/archive/2008/12/02/google-testing-blog-clean-code-talks.aspx#feedback 3 http://blogs.ugidotnet.org/bmatte/comments/commentRss/94828.aspx http://blogs.ugidotnet.org/bmatte/services/trackbacks/94828.aspx Usare RhinoMocks per testare applicazioni multithreading http://blogs.ugidotnet.org/bmatte/archive/2008/08/25/usare-rhinomocks-per-testare-applicazioni-multithreading.aspx <p>Interessato dal post di Antonio sull'uso del <a href="http://blogs.ugidotnet.org/AntonioGanci/archive/2008/06/06/92951.aspx">TDD per progettare applicazi multithreading</a> ho deciso di riprovare senguendo il percorso logico da lui espresso, usando RhinoMocks come framework di mocking piuttosto che i mocks manuali usati da Antonio per non complicare il codice di test. La versione di RhinoMocks utilizzata è la 3.5 RC che potete scaricare a <a href="http://www.ayende.com/projects/rhino-mocks/downloads.aspx">questo url</a>, questa versione della libreria supporta la sintassi Arrange/Act/Assert permettendo di scrivere test molto più chiari, per maggiori info visitate <a href="http://www.ayende.com/wiki/Rhino+Mocks+3.5.ashx">questa pagina</a>. </p> <p>Adesso vediamo e commentiamo i vari metodi di test, cominciamo con il primo:</p> <div class="CodeFormatContainer"><style><!-- .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; } --></style> <div class="csharpcode"> <pre class="alt"><span class="lnum"> 1: </span><span class="kwrd">public</span> <span class="kwrd">void</span> When_Enqueue_A_Log_The_LogWriter_Is_Invoked()</pre> <pre><span class="lnum"> 2: </span>{</pre> <pre class="alt"><span class="lnum"> 3: </span> var log = <span class="kwrd">new</span> Log();</pre> <pre><span class="lnum"> 4: </span> var writer = MockRepository.GenerateMock&lt;ILogWriter&gt;();</pre> <pre class="alt"><span class="lnum"> 5: </span> var thread = <span class="kwrd">new</span> LogThread(writer);</pre> <pre><span class="lnum"> 6: </span> </pre> <pre class="alt"><span class="lnum"> 7: </span> thread.Enqueue(log);</pre> <pre><span class="lnum"> 8: </span> </pre> <pre class="alt"><span class="lnum"> 9: </span> writer.AssertWasCalled(x =&gt; x.Write((Arg&lt;Log&gt;.Is.Equal(log))));</pre> <pre><span class="lnum"> 10: </span>}</pre> </div> </div> <p>Le differenze sostaziali sono, alla riga 4 e 9, nella prima generiamo un mock dell'interfaccia ILogWriter e nella seconda "chiediamo" al mock object di verificare se è stato chiamato il metodo Write e se il parametro in ingresso era uguale a log. Proseguiamo con il secondo test:</p> <div class="CodeFormatContainer"><style><!-- .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; } --></style> <div class="csharpcode"> <pre class="alt"><span class="lnum"> 1: </span><span class="kwrd">public</span> <span class="kwrd">void</span> The_Calls_To_The_Writer_Are_Asynchronous()</pre> <pre><span class="lnum"> 2: </span>{</pre> <pre class="alt"><span class="lnum"> 3: </span> var log = <span class="kwrd">new</span> Log();</pre> <pre><span class="lnum"> 4: </span> var writer = MockRepository.GenerateMock&lt;ILogWriter&gt;();</pre> <pre class="alt"><span class="lnum"> 5: </span> var thread = <span class="kwrd">new</span> LogThread(writer);</pre> <pre><span class="lnum"> 6: </span> <span class="kwrd">int</span> writerCallCount = 0;</pre> <pre class="alt"><span class="lnum"> 7: </span> var resetEvent = <span class="kwrd">new</span> ManualResetEvent(<span class="kwrd">false</span>);</pre> <pre><span class="lnum"> 8: </span> </pre> <pre class="alt"><span class="lnum"> 9: </span> writer.Expect(x =&gt; x.Write((Arg&lt;Log&gt;.Is.Anything))).Do(<span class="kwrd">new</span> Action&lt;Log&gt;(l =&gt;</pre> <pre><span class="lnum"> 10: </span> {</pre> <pre class="alt"><span class="lnum"> 11: </span> writerCallCount++;</pre> <pre><span class="lnum"> 12: </span> resetEvent.WaitOne();</pre> <pre class="alt"><span class="lnum"> 13: </span> writerCallCount--;</pre> <pre><span class="lnum"> 14: </span> }));</pre> <pre class="alt"><span class="lnum"> 15: </span> </pre> <pre><span class="lnum"> 16: </span> thread.Enqueue(log);</pre> <pre class="alt"><span class="lnum"> 17: </span> <span class="kwrd">while</span> (writerCallCount == 0)</pre> <pre><span class="lnum"> 18: </span> {</pre> <pre class="alt"><span class="lnum"> 19: </span> Thread.Sleep(0);</pre> <pre><span class="lnum"> 20: </span> }</pre> <pre class="alt"><span class="lnum"> 21: </span> Assert.AreEqual(1, writerCallCount); </pre> <pre><span class="lnum"> 22: </span> resetEvent.Set();</pre> <pre class="alt"><span class="lnum"> 23: </span>}</pre> </div> </div> <p>La parte interessante in questo test sono le righe dalla numero 9 alla 14, dove istruisco il mock object. In pratica non faccio altro che "dire" al mock object "quando viene invocato il metodo Write con una qualsiasi istanza di Log, esegui la seguente Action". Il delegate passato come Action al metodo Do esegue la solita logica del mock manuale usato da Antonio. Per il resto il codice di test è sostanzialmente identico. La logica del terzo metodo di test è la solita quindi vi posto solo il codice:</p> <div class="CodeFormatContainer"><style><!-- .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; } --></style> <div class="csharpcode"> <pre class="alt"><span class="lnum"> 1: </span><span class="kwrd">public</span> <span class="kwrd">void</span> Two_Logs_Enqueued_Are_Served_One_At_Time()</pre> <pre><span class="lnum"> 2: </span>{</pre> <pre class="alt"><span class="lnum"> 3: </span> var writer = MockRepository.GenerateMock&lt;ILogWriter&gt;();</pre> <pre><span class="lnum"> 4: </span> var thread = <span class="kwrd">new</span> LogThread(writer);</pre> <pre class="alt"><span class="lnum"> 5: </span> <span class="kwrd">int</span> writerCallCount = 0;</pre> <pre><span class="lnum"> 6: </span> var resetEvent = <span class="kwrd">new</span> ManualResetEvent(<span class="kwrd">false</span>);</pre> <pre class="alt"><span class="lnum"> 7: </span> </pre> <pre><span class="lnum"> 8: </span> writer.Expect(x =&gt; x.Write((Arg&lt;Log&gt;.Is.Anything))).Do(<span class="kwrd">new</span> Action&lt;Log&gt;(l =&gt;</pre> <pre class="alt"><span class="lnum"> 9: </span> {</pre> <pre><span class="lnum"> 10: </span> writerCallCount++;</pre> <pre class="alt"><span class="lnum"> 11: </span> resetEvent.WaitOne();</pre> <pre><span class="lnum"> 12: </span> writerCallCount--;</pre> <pre class="alt"><span class="lnum"> 13: </span> }));</pre> <pre><span class="lnum"> 14: </span> </pre> <pre class="alt"><span class="lnum"> 15: </span> Log log1 = <span class="kwrd">new</span> Log();</pre> <pre><span class="lnum"> 16: </span> thread.Enqueue(log1);</pre> <pre class="alt"><span class="lnum"> 17: </span> <span class="kwrd">while</span> (writerCallCount &lt; 1)</pre> <pre><span class="lnum"> 18: </span> {</pre> <pre class="alt"><span class="lnum"> 19: </span> Thread.Sleep(0);</pre> <pre><span class="lnum"> 20: </span> }</pre> <pre class="alt"><span class="lnum"> 21: </span> </pre> <pre><span class="lnum"> 22: </span> Log log2 = <span class="kwrd">new</span> Log();</pre> <pre class="alt"><span class="lnum"> 23: </span> thread.Enqueue(log2);</pre> <pre><span class="lnum"> 24: </span> <span class="kwrd">while</span> (thread.RunningThreadCount &lt; 2)</pre> <pre class="alt"><span class="lnum"> 25: </span> {</pre> <pre><span class="lnum"> 26: </span> Thread.Sleep(0);</pre> <pre class="alt"><span class="lnum"> 27: </span> }</pre> <pre><span class="lnum"> 28: </span> </pre> <pre class="alt"><span class="lnum"> 29: </span> Assert.AreEqual(1, writerCallCount);</pre> <pre><span class="lnum"> 30: </span> </pre> <pre class="alt"><span class="lnum"> 31: </span> resetEvent.Set();</pre> <pre><span class="lnum"> 32: </span> <span class="kwrd">while</span> (writerCallCount &gt; 0)</pre> <pre class="alt"><span class="lnum"> 33: </span> {</pre> <pre><span class="lnum"> 34: </span> Thread.Sleep(0);</pre> <pre class="alt"><span class="lnum"> 35: </span> }</pre> <pre><span class="lnum"> 36: </span>}</pre> </div> </div> <p>In fine passiamo all'ultimo metodo di test:</p> <div class="CodeFormatContainer"><style><!-- .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; } --></style> <div class="csharpcode"> <pre class="alt"><span class="lnum"> 1: </span><span class="kwrd">public</span> <span class="kwrd">void</span> When_The_Writer_Throws_An_Exception_The_QueueLength_Is_Decreased()</pre> <pre><span class="lnum"> 2: </span>{</pre> <pre class="alt"><span class="lnum"> 3: </span> var writer = MockRepository.GenerateMock&lt;ILogWriter&gt;();</pre> <pre><span class="lnum"> 4: </span> var thread = <span class="kwrd">new</span> LogThread(writer);</pre> <pre class="alt"><span class="lnum"> 5: </span> <span class="kwrd">bool</span> isWriting = <span class="kwrd">false</span>;</pre> <pre><span class="lnum"> 6: </span> </pre> <pre class="alt"><span class="lnum"> 7: </span> writer.Expect(x =&gt; x.Write((Arg&lt;Log&gt;.Is.Anything))).Do(<span class="kwrd">new</span> Action&lt;Log&gt;(l =&gt;</pre> <pre><span class="lnum"> 8: </span> {</pre> <pre class="alt"><span class="lnum"> 9: </span> isWriting = <span class="kwrd">true</span>;</pre> <pre><span class="lnum"> 10: </span> <span class="kwrd">throw</span> <span class="kwrd">new</span> Exception();</pre> <pre class="alt"><span class="lnum"> 11: </span> }));</pre> <pre><span class="lnum"> 12: </span> </pre> <pre class="alt"><span class="lnum"> 13: </span> thread.Enqueue(<span class="kwrd">new</span> Log());</pre> <pre><span class="lnum"> 14: </span> <span class="kwrd">while</span> (!isWriting)</pre> <pre class="alt"><span class="lnum"> 15: </span> {</pre> <pre><span class="lnum"> 16: </span> Thread.Sleep(0);</pre> <pre class="alt"><span class="lnum"> 17: </span> }</pre> <pre><span class="lnum"> 18: </span> <span class="kwrd">while</span> (thread.RunningThreadCount &gt; 0)</pre> <pre class="alt"><span class="lnum"> 19: </span> {</pre> <pre><span class="lnum"> 20: </span> Thread.Sleep(0);</pre> <pre class="alt"><span class="lnum"> 21: </span> }</pre> <pre><span class="lnum"> 22: </span> Assert.AreEqual(0, thread.RunningThreadCount);</pre> <pre class="alt"><span class="lnum"> 23: </span>}</pre> </div> </div> <p>Anche qui la parte interessante è la configurazione del mock object, in questo test la sintassi del mock rimane uguale ai test precedenti, quello che cambia è la logica contenute nell' Action delegate passato al metodo Do, il quale scatena un' eccezione.</p> <p>Nel mio repository personale ho creato tre proggeti uno con la versione manuale dei mocks, uno che usa RhinoMocks ed in fine uno che usa Moq, il tutto è reperibile a questo url: <a title="https://makesimple.googlecode.com/svn/trunk/UnitTestMultithreading" href="http://makesimple.googlecode.com/svn/trunk/UnitTestMultithreading">http://makesimple.googlecode.com/svn/trunk/UnitTestMultithreading</a></p> <div class="wlWriterSmartContent" id="scid:0767317B-992E-4b12-91E0-4F059A8CECA8:5767b1a4-17c7-4a13-9163-61ed850b9f99" style="padding-right: 0px; display: inline; padding-left: 0px; padding-bottom: 0px; margin: 0px; padding-top: 0px">Technorati Tag: <a href="http://technorati.com/tags/Multithreading" rel="tag">Multithreading</a>,<a href="http://technorati.com/tags/Mocks" rel="tag">Mocks</a>,<a href="http://technorati.com/tags/UnitTest" rel="tag">UnitTest</a>,<a href="http://technorati.com/tags/TDD" rel="tag">TDD</a></div><img src="http://blogs.ugidotnet.org/bmatte/aggbug/93807.aspx" width="1" height="1" /> Matteo Baglini http://blogs.ugidotnet.org/bmatte/archive/2008/08/25/usare-rhinomocks-per-testare-applicazioni-multithreading.aspx Mon, 25 Aug 2008 12:19:32 GMT http://blogs.ugidotnet.org/bmatte/archive/2008/08/25/usare-rhinomocks-per-testare-applicazioni-multithreading.aspx#feedback 1 http://blogs.ugidotnet.org/bmatte/comments/commentRss/93807.aspx http://blogs.ugidotnet.org/bmatte/services/trackbacks/93807.aspx Google C++ Testing Framework http://blogs.ugidotnet.org/bmatte/archive/2008/07/16/google-c-testing-framework.aspx Nei miei projetti C# faccio un largo uso di test unitari, utilizzando <a href="http://www.nunit.org/index.php">NUnit</a> o <a href="http://www.codeplex.com/xunit">xUnit.NET</a>, so già che entro la fine dell'anno prenderò in consegna una grande applicazione C++, quindi in passato avevo dato un'occhiata veloce a quello che era il framework di  testing di riferimento: <a href="http://cppunit.sourceforge.net/cppunit-wiki">CppUnit </a>. <br /> Adesso Google ha rilasciato il proprio <a href="http://code.google.com/p/googletest/">framework </a>per fare testing automatico di codice C++, non l'ho ancora provato (...aaahhhh il tempo) ma sembra proprio ottimo!<br /> <br /> Fonte: <a href="http://www.infoq.com/news/2008/07/google-test">InfoQ: Announcing: New Google C++ Testing Framework</a><img src="http://blogs.ugidotnet.org/bmatte/aggbug/93426.aspx" width="1" height="1" /> Matteo Baglini http://blogs.ugidotnet.org/bmatte/archive/2008/07/16/google-c-testing-framework.aspx Wed, 16 Jul 2008 15:33:26 GMT http://blogs.ugidotnet.org/bmatte/archive/2008/07/16/google-c-testing-framework.aspx#feedback 4 http://blogs.ugidotnet.org/bmatte/comments/commentRss/93426.aspx http://blogs.ugidotnet.org/bmatte/services/trackbacks/93426.aspx Evolutionary Databases - Design and Deployment http://blogs.ugidotnet.org/bmatte/archive/2008/03/26/evolutionary-databases---design-and-deployment.aspx <p>Esistono diversi tools di Refactoring, in continua evoluzione, che aiutano uno sviluppatore agile ad evolvere in maniera incrementale il proprio codice. Lo stesso non si può dire per quanto riguarda il refactoring del database, si perchè anche quest'ultimo vuole la sua parte, quindi anche il design della del database deve evolvere in maniera incrementale, user story dopo user story. A questo punto nasce il probleblema di gestire questa naturale evoluzione all'interno del processo di sviluppo software.</p> <p>L'approccio generalmente usato è quello di partire da uno script di base di crazione del database, da lì in poi per ogni user story che andrà a "toccare" il database verrà generato un script di aggiornamento che contiene le modifiche dalla versione precedente ed eventauli migrazione dati. Allo stesso modo lo script deve essere in grado di far "regredire" la struttura del database alla una release precedente. Tutti gli script seguenti a quello base saranno numerati, naturalmente in ordine crescente. Nel processo di build del progetto ci sarà un task che si occuperà di verificare la versione corrente del database e di eseguirein ordine tutti gli script necessari ad allineare il database alla nuova versione. </p> <p>Esistono diversi tools in rete sia per il mondo .NET che per altri come Java e Ruby. Tutti si basano sulla filosofia che ho descritto prima, però hanno forme diverse di implementazione. Esiste una categoria di tools che utilizza gli script SQL, un'altra che utilizza file XML ed un'ultima categoria che utilizza file di codice (C#,VB.NET,ecc). Di seguito un elenco (sicuramente non completo) dei tools, basati su script SQL: </p> <p>- dbdeploy.NET - <a href="http://sourceforge.net/projects/dbdeploy-net/">http://sourceforge.net/projects/dbdeploy-net/</a> <br /> - dbdeploy - <a href="http://dbdeploy.com">http://dbdeploy.com</a></p> <p>basati su XML: </p> <p>- MIGRATEdb - <a href="http://migratedb.sourceforge.net/">http://migratedb.sourceforge.net/</a> <br /> - LiquiBase - <a href="http://www.liquibase.org/">http://www.liquibase.org/</a></p> <p>basati su codice: </p> <p>- ActiveRecordMigration  - <a href="http://wiki.rubyonrails.com/rails/pages/UnderstandingMigrations">http://wiki.rubyonrails.com/rails/pages/UnderstandingMigrations</a> <br /> - SubSonic Migrate - <a href="http://blog.wekeroad.com/2007/10/03/subsonic-migrate-me/">http://blog.wekeroad.com/2007/10/03/subsonic-migrate-me/</a> <br /> - RikMigrations - <a href="http://www.codeplex.com/RikMigrations">http://www.codeplex.com/RikMigrations</a> <br /> - Migrator.NET - <a href="http://code.google.com/p/migratordotnet/">http://code.google.com/p/migratordotnet/</a></p> <p>Tutti questi tools si assomigliano molto, quello che secondo me può determinare la scelta di uno piuttosto che di un'altro è lo skill di chi dovrà creare l'update del database. Sicuramente se a modificare il database sarà un DBA, il quale ha una profonda conoscenza di SQL, il tool migliore sarà uno di quelli basati su script. Diversamente se sarà uno sviluppatore il tool più adatto sarà uno quelli basati su codice. Resta infine la categoria dei tools basati su file XML, a mio modesto parere, un ibrido senza nessun vantaggio, dato che richiede di racchiudere script SQL all'interno di appositi tag XML, complicandone la leggibilità. </p> <p>Io personalmente ho iniziato ad usare dbdeploy.NET, consigliatomi da <a href="http://blogs.ugidotnet.org/ABS/Default.aspx">Marco Abis</a>, che ringrazio. In fine segnalo questo <a href="http://trgoodwin.blogspot.com/2008/01/using-dbdeploynet.html" target="_blank">post-guida</a> all'uso di dbdeploy.NET e questo white paper <a href="http://dbdeploy.com/documentation/taking-control-of-your-database-development-white-paper/" target="_blank">Taking Control of your Database Development</a>,utile a prescindere dal tool usato.</p> <div class="wlWriterEditableSmartContent" id="scid:0767317B-992E-4b12-91E0-4F059A8CECA8:083152f7-fcc2-4e7a-b20f-990bb6c09501" style="margin: 0px; padding: 0px; display: inline;">Technorati Tag: <a href="http://technorati.com/tags/Refactoring" rel="tag">Refactoring</a>,<a href="http://technorati.com/tags/Agile" rel="tag">Agile</a>,<a href="http://technorati.com/tags/Database" rel="tag">Database</a></div><img src="http://blogs.ugidotnet.org/bmatte/aggbug/91872.aspx" width="1" height="1" /> Matteo Baglini http://blogs.ugidotnet.org/bmatte/archive/2008/03/26/evolutionary-databases---design-and-deployment.aspx Wed, 26 Mar 2008 14:09:00 GMT http://blogs.ugidotnet.org/bmatte/archive/2008/03/26/evolutionary-databases---design-and-deployment.aspx#feedback 5 http://blogs.ugidotnet.org/bmatte/comments/commentRss/91872.aspx http://blogs.ugidotnet.org/bmatte/services/trackbacks/91872.aspx