Unit Testing. Giovanni Lagorio lagorio@disi.unige.it



Похожие документы
Test di unità con JUnit4

Uso di JUnit. Fondamenti di informatica Oggetti e Java. JUnit. Luca Cabibbo. ottobre 2012

Tipi primitivi. Ad esempio, il codice seguente dichiara una variabile di tipo intero, le assegna il valore 5 e stampa a schermo il suo contenuto:

Funzioni in C. Violetta Lonati

3 - Variabili. Programmazione e analisi di dati Modulo A: Programmazione in Java. Paolo Milazzo

Inizializzazione, Assegnamento e Distruzione di Classi

Modulo 4: Ereditarietà, interfacce e clonazione

Esercizio 1: trading on-line

Programmazione a Oggetti Modulo B

Guida all uso di Java Diagrammi ER

Architettura MVC-2: i JavaBeans

Siti web centrati sui dati Architettura MVC-2: i JavaBeans

Soluzione dell esercizio del 2 Febbraio 2004

12 - Introduzione alla Programmazione Orientata agli Oggetti (Object Oriented Programming OOP)

10 - Programmare con gli Array

Sviluppo software guidato dal testing. metodologie e strumenti

Concetto di Funzione e Procedura METODI in Java

Componenti in.net, Dependency Injection e Software testabile. Giovanni Lagorio lagorio@disi.unige.it

MANUALE PARCELLA FACILE PLUS INDICE

La gestione dell input/output da tastiera La gestione dell input/output da file La gestione delle eccezioni

Progettazione : Design Pattern Creazionali

Programmazione Orientata agli Oggetti in Linguaggio Java

Esempi di algoritmi. Lezione III

INSTALLAZIONE NUOVO CLIENT TUTTOTEL (04 Novembre 2014)

Gestione Risorse Umane Web

Esercizi su. Funzioni

Siamo così arrivati all aritmetica modulare, ma anche a individuare alcuni aspetti di come funziona l aritmetica del calcolatore come vedremo.

Convertitori numerici in Excel

IL MIO PRIMO SITO: NEWS

Informatica per la comunicazione" - lezione 13 -

13 - Gestione della Memoria nella Programmazione Orientata agli Oggetti

Modulo 4 Il pannello amministrativo dell'hosting e il database per Wordpress

Capitolo 3. L applicazione Java Diagrammi ER. 3.1 La finestra iniziale, il menu e la barra pulsanti

Uno dei pregi di Java è quello di integrare la documentazione con il codice stesso Formato dei commenti:

Introduzione a Dev-C++

La progettazione dell interfaccia HCI. Fabio Vitali

4 3 4 = 4 x x x 10 0 aaa

Tricks & Tips. [Access] Tutorial - ActiveX - Controllo Tree View. - Michele de Nittis - Versione: 1 Data Versione: venerdì 30 agosto 2002

Programmazione a Oggetti Lezione 10. Ereditarieta

Per chi ha la Virtual Machine: avviare Grass da terminale, andando su Applicazioni Accessori Terminale e scrivere grass

Visual Basic.NET La Gestione degli Errori di Federico BARBATI

Integrazione InfiniteCRM - MailUp

Settaggio impostazioni tema. Cliccando nuovamente su aspetto e poi su personalizza si avrà modo di configurare la struttura dinamica della template.

Traccia di soluzione dell esercizio del 25/1/2005

Product Shipping Cost Guida d'installazione ed Utilizzo

Programmazione Orientata agli Oggetti in Linguaggio Java

Office 2007 Lezione 02. Le operazioni più

PowerPoint. Guida introduttiva

SOMMARIO Coda (queue): QUEUE. QUEUE : specifica QUEUE

Per scrivere una procedura che non deve restituire nessun valore e deve solo contenere le informazioni per le modalità delle porte e controlli

FPf per Windows 3.1. Guida all uso

Tale attività non è descritta in questa dispensa

Object Oriented Programming

Il web server Apache Lezione n. 3. Introduzione

costruttori e distruttori

UML Diagrammi delle classi. UML Diagramma classi 1

Appunti di Interazione Uomo Macchina Principi di progettazione per il web I wireframe

Parola chiave extends

ShellExcel. Una domanda contiene i riferimenti (#A, #B, #C) alle celle che contengono i dati numerici del

Registri RMI. Massimo Merro Univ. Verona Programmazione di Rete 90 / 247

Mac Application Manager 1.3 (SOLO PER TIGER)

A intervalli regolari ogni router manda la sua tabella a tutti i vicini, e riceve quelle dei vicini.

Java: Compilatore e Interprete

Concetti Base Eccezioni Eccezioni e Metodi Gerarchia di Eccezioni. Java: Eccezioni. Damiano Macedonio

La manutenzione come elemento di garanzia della sicurezza di macchine e impianti


Corso di Informatica

I/O su Socket TCP: read()

IL MIO PRIMO SITO NEWS USANDO GLI SCHEDARI

Testo Esercizio. Un modello è ragionevole quando contiene queste tre caratteristiche.

Visibilità dei Membri di una Classe

Joomla! 2.5:Utenti e permessi - Il wiki di Joomla.it

Guida Rapida all uso del License Manager di ROCKEY4Smart (V )

risulta (x) = 1 se x < 0.

PROCEDURA INVENTARIO DI MAGAZZINO di FINE ESERCIZIO (dalla versione 3.2.0)

MANUALE D'USO DEL PROGRAMMA IMMOBIPHONE

STAMPA UNIONE DI WORD

Introduzione alla programmazione in C

Oltre il diritto d autore: Creative Commons. Formazione Volontari SCN 2010 Mediateca Centro Linguistico di Ateneo - febbraio 2010

dall argomento argomento della malloc()

Come modificare la propria Home Page e gli elementi correlati

file:///c:/formazione/photoshop-webmaster-uffici/doc/guida-winzip.htm Guida a Winzip

Realizzazione di una classe con un associazione

RIFERIMENTI ATTORI GLOSSARIO. ERRORI COMUNI REV. REQUISITI INGEGNERIA DEL SOFTWARE Università degli Studi di Padova

Introduzione. Java. Composizione. Esempio -- composizione. G. Prencipe È qualcosa che abbiamo già visto varie volte

MANUALE UTENTE Fiscali Free

Mobilità di Codice. Massimo Merro Programmazione di Rete 128 / 144

Main System Monitor Keyboard

Introduzione JDBC interfaccia java.sql driver caricare i driver

f(x) = 1 x. Il dominio di questa funzione è il sottoinsieme proprio di R dato da

Corso Eclipse. Prerequisiti. 1 Introduzione

Airone Gestione Rifiuti Funzioni di Esportazione e Importazione

GHPPEditor è un software realizzato per produrre in modo rapido e guidato un part program per controlli numerici Heidenhain.

Entrare nel sistema. Clicca su Entra per entrare nel sistema. PAGINA 1

11/02/2015 MANUALE DI INSTALLAZIONE DELL APPLICAZIONE DESKTOP TELEMATICO VERSIONE 1.0

Транскрипт:

Unit Testing Giovanni Lagorio lagorio@disi.unige.it

Licenza Questi lucidi sono rilasciati sotto la licenza Creative Commons Attribuzione-Non commerciale-non opere derivate 3.0 Unported. Per leggere una copia della licenza visita il sito web http://creativecommons.org/licenses/by-nc-nd/3.0/ o spedisci una lettera a Creative Commons, 171 Second Street, Suite 300, San Francisco, California, 94105, USA. In due parole, possono essere liberamente usati, copiati e distribuiti purché: 1. Venga citata la fonte originale 2. Non vengano usati in ambito commerciale 3. Non vengano modificati in nessun modo

Definizione Uno unit test è un frammento di codice (tipicamente, un metodo) che invoca un altro frammento di codice e poi verifica se delle condizioni sono soddisfatte Se non lo sono, lo unit-test fallisce Una unità (unit) è un metodo o una funzione, spesso chiamata System Under Test (SUT) E molto importante che la unit sia piccola: quando un test fallisce è immediato sapere dov è il problema

Caratteristiche necessarie Automatico e ripetibile Deve bastare un click per testare tutto il sistema Si usano dei framework e test-runner Indipendente Non deve richiedere particolari configurazioni Veloce (non è un integration-test!) Da implementare Da eseguire Usabile da chiunque È documentazione in forma di codice

Test Driven Development Classicamente, prima si scrive il codice e poi lo si testa Nel TDD prima si scrivono gli unit-test, che ovviamente falliranno, poi si scriverà il codice per farli passare I test vengono poi usati per il regression testing e facilitano (=danno fiducia per) il refactoring Sistemato il codice (corretto e pulito), si ricomincia scrivendo nuovi test...

Refactoring E la modifica di un frammento di codice che non ne modifica la funzionalità Serve a ottenere codice più pulito, facile da leggere, modificare e debuggare Un esempio classico di refactoring è rinominare un metodo Gli IDE supportano sempre di più il refactoring Per VS, ReSharper potenzia questo e altri aspetti

Testing Framework Ne esistono tanti Useremo NUnit, www.nunit.org Fa parte della famiglia xunit (JUnit per Java, CppUnit per C++ e così via) Free Ben documentato Supportato da ReSharper http://www.jetbrains.com/resharper/

Convenzioni Per ogni progetto Prj che vogliamo testare, creiamo una class library Prj.Tests Per ogni classe C (da testare), creiamo una classe corrispondente CTests in <Prj>.Tests Per ogni metodo m scriveremo tanti metodi: <m><scenario><comporamento atteso> Per esempio, Parse_ValidArgs_Returns10, Parse_NullArgs_ThrowsException,...

Poiché usiamo NUnit... Al progetto di test dobbiamo aggiungere il riferimento a nunit.framework (Add Reference.NET...) oltre, ovviamente, al progetto da testare Alle classi (di Test) l attributo [TestFixture] del namespace NUnit.Framework Ai metodi l attributo [Test]

ReShaper riconosce gli Unit Test Le icone colorate sul bordo indicano gli unit-test, che possono essere eseguiti da VS Per il momento non preoccupiamoci del corpo dei metodi...

Test Runner di ReSharper

Test runner di NUnit Se non vogliamo/possiamo usare ReSharper Procedimento analogo, lanciamo (il test runner di) NUnit, scegliamo la DLL e lanciamo i test...

Requisiti per usare un test runner Poiché il test runner deve poter invocare i test in modo totalmente automatico: Le classi e i metodi di test devono essere public Le classi di test devono avere un costruttore senza parametri ed essere annotate [TestFixture] I metodi di test devono essere void, senza parametri e annotati [Test] (vedremo in seguito che non è vero al 100%)

Struttura di un (metodo di) unit test 1. Creare e inizializzare gli oggetti coinvolti 2. Invocare il metodo da testare 3. Asserire che qualcosa sia come ci si aspetta Per esempio: [Test] public void Parse_ValidArgString42_Returns42AsInt() { } var returnvalue = new Parser().Parse("42"); Assert.That(returnValue, Is.EqualTo(42));

Stili di Assert Classico: Assert.AreEqual(returnValue, 42); invocando Is.../Are... di Assert A vincoli (da NUnit 2.4) discorsivo : Assert.That(returnValue, Is.EqualTo(42)); invocando sempre That e passare come secondo parametro un vincolo, tipicamente ottenuto invocando un metodo statico di Is

Classe Assert Tutti i metodi di assert hanno un parametro opzionale (in realtà, una versione in overload: i parametri opzionali arriveranno con C# 4) per specificare un messaggio di errore Per esempio, Assert.That(returnValue, Is.EqualTo(43), "wrong answer!"); Assert.AreEqual(returnValue, 43, "wrong answer!");

Quante chiamate ad Assert? Una per test: la prima che fallisce (solleva un eccezione e) impedisce l esecuzione delle altre Il seguente non è un test ma sono (dovrebbero essere) tre: [Test] public void Parse_ValidArgString_ReturnsInts() { Assert.That(_parser.Parse("42"), Is.EqualTo(42)); Assert.That(_parser.Parse("1"), Is.EqualTo(1)); Assert.That(_parser.Parse("0"), Is.EqualTo(0)); } Nota: non mischiare mai l esecuzione del test e le asserzioni, qui l ho fatto solo per farlo stare nella slide

Test parametrici Permettono di generare diversi test usando un solo metodo, per esempio: [TestCase("42", 42)] [TestCase("0", 0)] [TestCase("1", 1)] public void Parse_ValidArg(string a, int r) { Assert.That(_parser.Parse(a), Is.EqualTo(r)); } I test vengono generati a load-time da NUnit Purtroppo, non (ancora?) supportati da ReSharper

Test parametrici L attributo [TestCase] identifica un metodo come test e gli fornisce i valori dei parametri E anche possibile specificare i valori sui singoli parametri (attributi [Random], [Range] e [Values]) e combinarli in vari modi (attributi [Combinatorial], che è il default, e [Sequential]. E` in alfa [Pairwise]) [Test] public void MyTest([Values(1,2,3)] int x, [Random(-1.0, 1.0, 5)] double d) { // eseguito 15 volte, per ogni x (1,2,3) // cinque valori casuali fra -1 e 1 }

Ovvio workaround per ReSharper private void Parse_ValidArg(string s, int expectedvalue) { int retvalue = this._parser.parse(s); Assert.That(retValue, Is.EqualTo(expectedValue)); } [Test] public void Parse_ValidArg42AsString_Returns42AsInt() { this.parse_validarg("42", 42); } [Test] public void Parse_ValidArg0AsString_Returns0AsInt() { this.parse_validarg("0", 0); } [Test] public void Parse_ValidArg1AsString_Returns1AsInt() { this.parse_validarg("1", 1); }

Se ci aspettiamo un eccezione? Annotiamo il metodo con [ExpectedException] Per esempio, [Test] [ExpectedException(typeof(ArgumentNullException))] public void Parse_NullArg_ThrowsArgumentNullException() { new Parser().Parse(null); } Attenzione: il match è esatto (se viene sollevata una sottoclasse di quella specificata, il test fallisce)

Workaround per quando ci aspettiamo un eccezione o una sua sottoclasse: [Test] public void Parse_NullArg_ThrowsArgumentNullException() { try { new Parser().Parse(null); Assert.Fail("Expecting ArgumentNullException"); } catch (ArgumentNullException) {} }

SetUp e TearDown Poiché ogni test deve essere indipendente e l ordine di esecuzione non deve influenzarne l esito, ogni test deve allocare e rilasciare risorse Le parti comuni (a tutti i test) possono essere inserite in metodi di SetUp/TearDown I metodi annotati con [SetUp] vengono eseguiti prima di ogni test I metodi annotati con [TearDown] vengono eseguiti dopo ogni test Per esempio...

Esempio SetUp/TearDown private Parser _parser; [SetUp] public void Init() { _parser = new Parser(); } [TearDown] public void CleanUp() { _parser = null; } [Test] public void Parse_ValidArgString42_Returns42AsInt() { var returnvalue = _parser.parse("42"); Assert.That(returnValue, Is.EqualTo(42)); } //...

Altro su SetUp/TearDown In caso di ereditarietà (della classe di Test): Gli attributi di Setup/TearDown vengono ereditati I metodi SetUp/TearDown della classe base vengono invocati prima di quelli della derivata (che, chiaramente, non devono invocare quelli base) [SetUpTestFixture] e [TearDownTestFixture] permettono di annotare metodi che vengono eseguiti prima dell esecuzione del primo test (risp. dopo l esecuzione dell ultimo)

Annotazione [Ignore] L annotazione [Ignore] indica una class o dei test da ignorare. Per esempio, [Ignore("Incomplete test")][test] public void Foo() {}

Categorie E possibile associare delle categorie ai test, tramite l annotazione [Category] In questo modo diventa possibile eseguire solo i test di alcune categorie

Come testare le classi internal? Poiché le classi di test sono in un assembly diverso da quello delle classi testate, dobbiamo ricorrere a un attributo in AssemblyInfo.cs: [assembly:internalsvisibleto("tap_unittesting.tests")]

Unit Testing nel mondo reale... Assert Unit Test SUT

Stub e Mock Assert Stub Unit Test SUT Mock Ok

Stub e Mock Stub e Mock sono oggetti che, durante il testing, sostituiscono gli oggetti veri (le dipendenze del SUT) facendo il minimo indispensabile I mock registrano le interazioni per una successiva verifica (quante volte è stato chiamato il metodo m?) Usiamo: gli stub per testare lo stato finale (per esempio, la proprietà counter è maggiore di zero? ) i mock per testare le interazioni fra oggetti (per esempio, il metodo chiama Close sul parametro di tipo IConnection? )

Stub vs Mock Concetti simili (e spesso confusi) poiché mock stub che registra le interazioni La differenza fondamentale è che uno stub non può far fallire un test, un mock sì Assert su valore di ritorno o stato SUT Stub Assert su aspettative del Mock object Mock Unit Test SUT Stub Unit Test SUT Stub Stub Stub

Cosa usare? Stub o Mock? Spesso usati assieme Un mock, tanti stub (se servono tanti mock il test sta probabilmente facendo troppo) Preferire gli stub, ovvero la verifica state-based (invece di interaction-based), quando possibile in questo modo i test dipendono molto meno dall implementazione

Mock framework Scrivere a mano stub e mock è facile, ma noioso e una grossa perdita di tempo I mock framework sono librerie che permettono di creare mock/stub (dinamici) con pochissime linee di codice Ne esistono diversi, il più famoso per.net è probabilmente RhinoMocks Noi useremo Moq: http://code.google.com/p/moq/downloads/list

Come testare BulkEmailSender? Lo state-based testing è piuttosto difficile: come verificare che una mail è davvero stata spedita? E, come già detto, ma siamo sicuri di volerle spedire durante il testing?!?

Rivediamo il codice public class BulkEmailSender { private readonly IEmailSender _emailsender; private readonly string _footer; public BulkEmailSender(IEmailSender emailsender, string footer) { this._emailsender = emailsender; this._footer = footer; } } public void SendEmail(List<string> addresses, string body) { if (addresses == null) throw new ArgumentNullException("addresses"); if (body == null) throw new ArgumentNullException("body"); foreach (var a in addresses) { if (!this._emailsender.sendemail(a, body + this._footer)) throw new Exception("Cannot send email"); } }

Testiamo BulkEmailSender... [TestFixture] public class BulkEmailSenderTests { [Test] [ExpectedException(typeof(Exception))] public void SendEmail_WhenEmailSenderFails_ThrowsException() { var mock = new Mock<IEmailSender>(); var bulk = new BulkEmailSender(mock.Object, string.empty); var addresses = new List<string> { "a@a.com" }; bulk.sendemail(addresses, string.empty); } } [Test] public void SendEmail_PassingThreeAddresses_SendsSuccessfully() { var mock = new Mock<IEmailSender>(); var bulk = new BulkEmailSender(mock.Object, string.empty); mock.setup(es => es.sendemail(it.isany<string>(), It.IsAny<string>())).Returns(true); var addresses = new List<string> { "a@a.com", "b@b.org", "c@c.info" }; bulk.sendemail(addresses, string.empty); mock.verify(es => es.sendemail(it.isany<string>(), It.IsAny<string>()), Times.Exactly(3)); mock.verify(es => es.sendemail("b@b.org", It.IsAny<string>()), Times.Once()); // vale il discorso fatto per Assert: le due Verify dovrebbero stare // in due test separati }

Attenzione! var mock = new Mock<IEmailSender>(); Quello che Moq chiama Mock è un mockbuilder, il vero stub/mock (che si passa alla classe da testare) è contenuto nella proprietà Object del Mock var bulk = new BulkEmailSender(mock.Object,...

Setup e Verify Setup indica al mock cosa rispondere Altrimenti risponde false per bool, 0 per int, ecc A meno che venga costruito con MockBehavior.Strict Verify verifica che le invocazioni/accessi a proprietà/ecc siano avvenute Entrambe usano le per descrivere lo scenario; nei parametri di tipo T: Un valore di tipo T rappresenta se stesso It.IsAny<T>() rappresenta qualsiasi valore It.Is<T>(predicato) rappresenta i valori che rispettano il predicato; esempio: It.Is<string>(s => s.length > 3) Inoltre: It.IsInRange e It.IsRegex

Returns e Throws Returns specifica il valore di ritorno, per esempio: mock.setup(...).returns(true); Throws specifica l eccezione da sollevare, per esempio: mock.setup(...).throws<exception>(); mock.setup(...).throws(new Exception("bla bla"));

Verifica di proprietà Non ha molto senso, ma consideriamo come esempio: public interface IEmailSender { bool SendEmail(string to, string body); string Subject { get; set; } } Se vogliamo verificare che la proprietà Subject venga letta o scritta possiamo usare

Verificare che una proprietà venga......letta mock.verifyget(es=>es.subject);...scritta mock.verifyset(es=>es.subject);...scritta con un certo valore mock.verifyset(es=>es.subject = "test");...scritta con un valore che rispetta un predicato mock.verifyset(es=>es.subject = It.Is<string>(s=>s.Length>0));

Settare il valore letto mock.setup(foo => foo.name).returns("bar"); // auto-mocking hierarchies (a.k.a. recursive mocks) mock.setup(foo =>foo.bar.baz.name).returns("baz"); // verify the setter mock.verifyset(foo => foo.name = "foo");

Implementare più interfacce Se il mock deve implementare più interfacce, basta usare il metodo As: var mock = new Mock<IEmailSender>(); // mock.setup... (per il tipo IEmailSender) var mockasifoo = mock.as<ifoo>(); // mockasifoo.setup... (per il tipo IFoo) Debug.Assert(mock.Object==mockAsIFoo.Object);

Callback Se vogliamo eseguire del codice quando qualcosa accade (per esempio, l invocazione di un metodo) possiamo usare una callback: int counter = 0; mock.setup(es => es.sendemail(/*...*/)).returns(true).callback(() => ++counter); Nota: la della callback può anche ricevere gli argomenti dell invocazione corrispondente

Lazy evaluation sul Returns // returning different values on each invocation var mock = new Mock<IFoo>(); var calls = 0; mock.setup(foo => foo.bar("ping")).returns(() => calls).callback(() => calls++); // returns 0 on first invocation, 1 on the next, Console.WriteLine(mock.Object.Bar("ping"));

Altro Eventi... li gestisce, ma ne riparliamo dopo che avremo visto cosa sono gli eventi