.NET & Software Testing Andrea Saltarello Software Architect – Managed Designs S.r.l. http://blogs.ugidotnet.org/pape Sponsor Parliamo di… • • • • Design/Test/Interaction patterns Unit Testing Mock Objects Visual Studio 2005 If it ain’t tested, it’s broken (cit. Bruce Eckel) Software Testing 101 Possiamo dichiarare funzionante il nostro software solo se siamo in grado di dimostrarlo Il funzionamento si dimostra mediante appositi test Utilizzare un software non è considerabile una pratica di test (AntiPattern TestByReleasing, TestingByPokingAround, …) Tipologie di test Esistono differenti tipologie di test: • Programmer Test/Technology facing (definiti anche unit test) • Customer Test/Business facing (definiti anche acceptance test o functional test) • Stress/Load Test • Usage Test • … Noi parleremo di Unit Testing Unit Testing 101 Lo Unit Testing è una tipologia di test automatico, basata sulla possibilità di sottoporre a test i singoli componenti di un sistema per verificarne la capacità di soddisfare i requisiti. I suddetti test sono definiti “unitari”, e sono programmi batch che utilizzano le nostre classi inviando messaggi prefissati in modo da poter verificare la risposta della classe stessa Toolkit di Unit Testing Esistono (almeno) 2 affermati toolkit di unit testing per .NET, entrambi ispirati alla suite xUnit ideata da Kent Beck ed Erich Gamma: • NUnit • csUnit …Noi usiamo NUnit NUnit 101 NUnit è composto da un assembly (contenente il framework di unit testing) e da una applicazione host in grado di eseguire i test e produrre un report. Per realizzare i test: • Creare una class library che conterrà i test • Definire una classe, e implementare i test sotto forma di metodi • Decorare i metodi di test con l’attributo TestAttribute • Decorare la classe che contiene i test con l’attributo TestFixture • Eseguire l’assembly dei test mediante l’applicazione host Unit Testing - Asserzioni Pratica comune nello unit testing è avvalersi delle asserzioni, ossia della capacità di definire delle condizioni che verificano la correttezza di funzionamento del codice in esame NUnit permette di definire delle asserzioni mediante la classe Assert Se l’asserzione non è soddisfatta, il test fallisce [Test] public void TestSum() { int number = 4; Assert.AreEqual(2, (int) Math.Round(number)); } NUnit: un assembly con gli “Attributi” La libreria contiene differenti attributi, tra i quali: • • • • • • TestFixture Test SetUp TearDown ExpectedException Ignore [Test] [ExpectedException(typeof(ArgumentNullException))] public void TestSum() { int number = int.Parse(null); } Houston, abbiamo un problema! • Il test unitario insiste su uno specifico componente • Un componente interagisce con altri componenti, eventualmente residenti su layer differenti • Il fallimento di un componente esterno può provocare il fallimento di un test Può essere conveniente isolare il componente sottoposto a test Isolare un componente Esistono alcuni pattern specifici, ispirati allo Special Case: • Stub/Shunt • Fake Objects • Mock Objects Diamo anche una occhiata ad Inversion of Control (a.k.a. Dependency Injection) Stub/Shunt The most simple implementation of an interface. Tipically, one might implement every method call by throwing a runtime exception. Fake Object A class with methods that return a fixed value or values that can either be hardcoded or set programmatically Mock Objects An object which, in addition to implementing the interface (like a Stub/Shunt) and returning meaningful values (like a Fake), allows for verification that the correct calls were made upon the object, perhaps in the correct order. Creare Mock Object Esistono toolkit free appositi: • DotNetMock • NMock …Noi usiamo NMock NMock La classe DynamicMock: • usa CodeDom e Reflection per creare dinamicamente un Mock Object DynamicMock mockGenerator = new DynamicMock(typeof(RealObjectType)); RealObjectType mockObject = (RealObjectType) mockGenerator.MockInstance; • Permette di “istruire” il Mock mediante metodi quali SetupResult e ExpectAndReturn mockGenerator.SetupResult(“MethodName”, 42, new Type[]{}); int result = mockObject.MethodName(); Fake vs. Mock Secondo Ward Cunningham, dovremmo preferire un Mock se: 1. real object has non-deterministic behavior 2. real object is difficult to set up 3. real object has behavior that is hard to cause (e.g., network error) (similar to 1) 4. real object is slow 5. real object has (or is) a UI 6. test needs to query the object, but the queries are not available in the real object (e.g., "was this callback called?") 7. real object acts "normal" most of the time, but once a month (or even less often) it does something "exceptional". We want UnitTests to make sure the rest of the system does the RightThing whether or not the object is acting "normal" or "exceptional". (Is this the same as #2 ?) 8. real object does not yet exist Ritorno al Futuro: Visual Studio 2005 I Team System di Visual Studio 2005 includono un toolkit di unit testing: • Strepitosamente simile a NUnit (Indovinello: dove lavora oggi James Newkirk, ex project leader di NUnit? ) • Dotato di funzionalità di code coverage Riferimenti Tools: • NUnit http://www.nunit.org • csUnit http://www.csunit.org • NMock http://www.nmock.org • DotNetMock http://dotnetmock.sourceforge.net • TestDriven.NET http://www.testdriven.net Documentazione: • NUnit vs. Team System • Mocks Aren't Stubs • Mock Stub Shunt • Mock Objects to the Rescue! Links http://www.ugidotnet.org http://forum.ugidotnet.org http://mobile.ugidotnet.org