Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
SlideShare a Scribd company logo
JUG Roma 26 th  September 2006 “ Embrace Unit Testing” Alessio Pace  alessio.pace [AT] gmail.com http://www.jroller.com/page/alessiopace
3 things about me.. Master Degree in Computer Engineering Java fan Just “Puccio” for the friends
About this presentation What you  will  find: definition and survey of the benefits of Unit Testing reusing material from the Web and books (see references at the end) a little coding example to get the feeling of Unit Testing using JUnit, EasyMock and XMLUnit What you  won't  find: an in-depth explanation about the internals of the above testing frameworks a Test Driven example
Agenda Introduction: The programmer life without  Unit Testing What is and what is not Unit Testing The benefits of Unit Testing Bad rumours about Unit Testing, and the answers to them Practical example in Java: JUnit  +  EasyMock JUnit + EasyMock + XMLUnit
INTRODUCTION
Programmer life  without  Unit Testing Write  a lot of  code...and someday something doesn't work / stops working Just fixing a bug is easy,  but  finding  it can be a nightmare: hours in front of the debugger interact like a monkey with the browser or reading the output on console Fixing it could break other parts The bug could appear again later
What is Unit Testing A  unit test  is an  automated and self-checked  procedure used to validate that a particular  module  of source code works correctly a module: a  single  Java class The procedure is to write  tests  for all the methods or functions of the module The class under test should be tested in  isolation
“A set of Unit Testing rules” A test is  not  a unit test if: It talks to the  database It communicates across the  network It touches the  file system It can't run at the same time as any of your other unit tests You have to do special things to your environment (such as editing config files) to run it You are not testing a class in isolation from other concrete classes
The benefits of Unit Testing (1) Isolate each part of the program and check that the individual parts work Strict, written contract that the piece of code must satisfy Allows to refactor code and make sure it still works correctly ( regression testing )
The benefits of Unit Testing (2) Simplifies  integration : testing the parts of a program first and then testing their integration Provides  documentation : clients and other developers can look at the unit tests to gain a basic understanding of the APIs and determine how to use the module to fit their needs Improves  code quality : code that cannot be easily tested is not factored properly
The benefits of Unit Testing (3) Separation of interface from implementation : a class may have some collaborator classes, testing that class can frequently lead into testing the other classes.. unit test should never go outside of its own class boundary: abstract an interface around the collaborator class, and implements that interface with a  mock object  (see later). This results in loosely coupled code, minimizing dependencies in the system
Bad rumours about Unit Testing “ It requires time, and I am always overscheduled” “ It is a waste of time: if I change my classes, I have to rewrite the tests!” “ My code is rock solid, I don't need tests!”
Answers to the bad rumours (1/2) The  “I'm in hurry”  issue leads to a vicious cycle: more pressure you feel -> less tests you write less tests you write -> less productive you are and the less stable your code becomes the less productive and accurate you are -> more pressure you feel
Answers to the bad rumours (2/2) Unit Testing  speeds up your development :  automated and self-checked code reduces the time to  manually  debug your code writing unit tests immediately after you code new features (or even better,  before ) lets you find bugs sooner and isolated (easy to correct) Unit Testing  lets you refactor safely: a change in code that causes a regression is immediately found
A short parenthesis on Refactoring Refactoring , a definition: “ A change made to the internal structure of software to make it easier to understand and cheaper to modify without changing its observable behaviour”   (“Refactoring”, Martin Fowler) Unit Testing and Refactoring go together: before refactoring a section of code you better have solid tests that cover it test, small change, test, small change, test, ..
PRACTICAL EXAMPLE
Let's get into a coding example! A practical example showing the very basics of: JUnit 4.x + EasyMock 2.2  JUnit 4.x + EasyMock 2.2 + XMLUnit See the References at the end for more tutorials and explanations about the internals of the frameworks
Our example:  a high level description Suppose we are  already  given a  IBookService   interface, which defines a method to retrieve an  IBook  by its code Suppose also we are  already  given an implementation of it:  RemoteBookService We want to implement and test: a trivial  CachedIBookService  implementation, which decorates an IBookService by  caching  subsequent calls for the same book code value
Our example:  UML Class Diagram We have to  implement  and  test  the  CachedBookService  class
Constructor implementation of  CachedBookService public class  CachedBookService  implements IBookService { private IBookService  remoteBookService ; public CachedBookService(IBookService bookService){ if(bookService == null){ throw new IllegalArgumentException(); } this.remoteBookService = bookService; } public IBookService getRemoteBookService() { return remoteBookService; } public IBook getBook(String code)  throws NoSuchElementException {   // implementation left out for the moment    throw new NotImplementedException(); } } Ok, let's  test the constructor
How to automatically test the constructor? We must check that: when passing a valid argument the object  state  after the construction is correct (the instance variable  remoteBookService  is set) when passing a  null  argument an  IllegalArgumentException  is thrown We will use a testing framework:  JUnit
JUnit: description JUnit  is just a general testing framework which provides  assertion methods You can use it to do  unit testing : testing a single class in isolation, eventually using  EasyMock  (or other mock objects frameworks) to mock collaborators classes You can use it to do  integration testing : testing the interactions of a class with  real  collaborator classes Almost a de-facto standard in the Java field (but have a look also at  TestNG ..)
Writing a JUnit 4.x test class  A test class is just a Java class Test methods must be annotated with  @Test Two configuration levels: before/after each test method call ( @Before  /  @After  annotated methods) before/after the test class is processed ( @BeforeClass  /  @AfterClass  annotated methods) Launch the test class from an IDE (“run as JUnit Test”) or the command line
Testing object state after constructor import static org.junit.Assert.*; import org.junit.Test; public class  CachedBookServiceTest  { @Test  public void  testConstructor () { // anonymous class to test in isolation CachedBookService constructor IBookService bookServiceParameter = new IBookService(){ public IBook getBook(String code) throws NoSuchElementException { return null; } }; CachedBookService cachedBookService =  new CachedBookService(bookServiceParameter); assertNotNull ("Assert not null", cachedBookService.getRemoteBookService()); /* 3 identical ways to test the reference value:  */ assertTrue ("Assert same reference",  bookServiceParameter == cachedBookService.getRemoteBookService()); // assertSame ("Assert same reference", bookServiceParameter,  cachedBookService.getRemoteBookService()); // assertEquals (“Assert same reference”, bookServiceParameter,  cachedBookService.getRemoteBookService()) }
Testing exception is thrown //  CachedBookServiceTest.java @Test(expected=IllegalArgumentException.class) public void  testConstructorWithNullArg () { // should throw IllegaArgumentException with null arg   new CachedBookService( null ); } }
Enjoy the green bar :-)
Now let's implement  getBook()  method public class CachedBookService implements IBookService { private IbookService remoteBookService; private Map<String, IBook> cache; public CachedBookService(IBookService bookService){ if(bookService == null){ throw new IllegalArgumentException(); } this.remoteBookService = bookService; this.cache = new HashMap<String, IBook>(); } public IBook getBook(String code) throws NoSuchElementException { IBook ibook = cache.get(code); if(ibook != null){ return ibook; }else{ IBook result = this.remoteBookService.getBook(code); this.cache.put(code, result); return result; } } Ok, let's  test  getBook()
How to test  getBook()? The class must be tested in isolation: we don't want to rely on a concrete IBookService collaborator object We must test the  interactions  of the class under test with the collaborator object: first request for an  IBook  code must be delegated to the  remoteBookService following requests for the same  IBook  code must be served from the  cache We are going to use JUnit with a Mock Objects framework:  EasyMock
What are Mock Objects? Most software units do not work alone, but collaborate with other units To test a unit in  isolation , the collaborators must be simulated Mock objects have the same interfaces of the objects they mimic a mock is configured to simulate the object behaviour that it replaces In contrast to a  stub ,  a mock object also verifies whether it is used as expected
Why are Mocks useful? Isolate bugs Depend on an interface and not on an implementation (which couldn't exist yet) Endo testing : not just testing the output of a method, but testing the interactions of a unit with the collaborator object(s) also called  “interaction based testing”  (as opposed to  “state based testing” )
EasyMock 2.2 EasyMock 2.2, two libraries: one to generate mocks for interfaces only ( easymock ) one to generate mocks for non-final concrete classes  and  interfaces ( easymock-classextension ) Benefits: hand writing mocks is not needed supports compiler check and  refactoring supports method return values and exceptions supports checking the order and count of method calls ...
How to get a mock object with EasyMock 2.2 create  a Mock Object for the interface  to simulate record the  expected behavior switch the mock object to  replay state perform the method call to the class under test that is supposed to interact with the mock obejct verify  the mock has been used correctly
Testing getBook() - 1 st  test method @Test public void  testGetBook () { // (1) create the mock for the collaborating object IBookService   mockBookService = EasyMock.createMock(IBookService.class); // a mock with no behaviour as return value of getBook() IBook mockBook = EasyMock.createMock(IBook.class); // (2) record the expected behaviour EasyMock.expect(mockBookService.getBook(&quot;id123&quot;)).andReturn(mockBook); // (3) switch the mock to replay state EasyMock.replay(mockBookService); CachedBookService cachedBookService =  new CachedBookService(mockBookService);  // (4) perform the call to the class under test which interacts with the mock IBook result = cachedBookService.getBook(&quot;id123&quot;); assertSame(“Assert same reference”, mockBook, result); // (5) verify the mock has been used as expected EasyMock.verify(mockBookService); }
Testing getBook() - 2 nd  test method @Test public void  testGetBookTwoDifferentCodes () { // (1) create the mock for the collaborating object IBookService   mockBookService = EasyMock.createMock(IBookService.class); // a mock with no behaviour as return value of getBook() IBook mockBook = EasyMock.createMock(IBook.class); IBook mockBook2 = EasyMock.createMock(IBook.class); // (2) record two expected calls EasyMock.expect(mockBookService.getBook(&quot;id123&quot;)).andReturn(mockBook); EasyMock.expect(mockBookService.getBook(&quot;id456&quot;)).andReturn(mockBook2); // (3) switch the mock to replay state EasyMock.replay(mockBookService); CachedBookService cachedBookService =  new CachedBookService(mockBookService);  // (4) perform the call to the class under test which interacts with the mock IBook result = cachedBookService.getBook(&quot;id123&quot;); assertSame(“Assert same reference”, mockBook, result); IBook result2 = cachedBookService.getBook(&quot;id456&quot;); assertSame(“Assert same reference”, mockBook2, result2); // (5) verify the mock has been used as expected EasyMock.verify(mockBookService); }
Testing getBook() - 3 rd  test method @Test public void  testGetSameBookTwoTimes () { IBookService mockBookService = EasyMock.createMock(IBookService.class); IBook mockBook = EasyMock.createMock(IBook.class); // record only one call EasyMock.expect(mockBookService.getBook(&quot;id123&quot;)).andReturn(mockBook); EasyMock.replay(mockBookService); CachedBookService cachedBookService = new CachedBookService(mockBookService); IBook result = cachedBookService.getBook(&quot;id123&quot;); assertSame(mockBook, result); IBook secondResult = cachedBookService.getBook(&quot;id123&quot;); assertSame(result, secondResult); EasyMock.verify(mockBookService); }
Adding an IBookXmlSerializer We are already given an  IBookXmlSerializer  interface to serialize an IBook to XML We have to  implement  and  test  a simple  IBookXmlSerializerImpl  which writes an XML representation of an IBook
IBookXmlSerializer
IBookXmlSerializerImpl public class  IBookXmlSerializerImpl  implements IBookXmlSerializer { public String serialize(IBook book) { StringBuilder sb = new StringBuilder(&quot;&quot;); sb.append(&quot;<book>&quot;); sb.append(&quot;  <code>&quot; + book.getCode() + &quot;</code>&quot;); sb.append(&quot;  <title>&quot; + book.getTitle() + &quot;</title>&quot;); sb.append(&quot;  <author>&quot; + book.getAuthor() + &quot;</author>&quot;); sb.append(&quot;</book>&quot;); return sb.toString(); } } Ok, let's  test  serialize()
How to test serialize()? We must check that the XML output is correct We must test the class in isolation: we can't rely on a concrete  IBook  implementation (we don't have it..) We are going to use JUnit, EasyMock and XMLUnit
Testing XML with XMLUnit “ XMLUnit extends JUnit to simplify unit testing of XML” “ It compares a control XML document to a test document or the result of a transformation, validates documents against a DTD, and compares the results of XPath expressions”
IbookXmlSerializerTest - Part 1 import org.custommonkey.xmlunit.XMLTestCase; public class IBookXmlSerializerImplTest { private IBookXmlSerializer serializer; private IBook book; private XMLTestCase xmlTestCase; @Before public void setUp() throws Exception { this.serializer = new IBookXmlSerializerImpl(); this.book = EasyMock.createMock(IBook.class); this.xmlTestCase = new XMLTestCase(); } // .... The method annotated with  @Before  is executed  before every @Test method execution
IbookXmlSerializerTest - Part 2 @Test public void testSerialize() throws Exception { final String code = &quot;id1&quot;; EasyMock.expect(this.book.getCode()).andReturn(code); final String title = &quot;Unit Testing&quot;; EasyMock.expect(this.book.getTitle()).andReturn(title); final String author = &quot;Mario Rossi&quot;; EasyMock.expect(this.book.getAuthor()).andReturn(author); // switch to replay state EasyMock.replay(this.book); String xml = this.serializer.serialize(book); this.xmlTestCase.assertXpathExists(&quot;/book&quot;, xml); this.xmlTestCase.assertXpathEvaluatesTo(code, &quot;/book/code&quot;, xml); this.xmlTestCase.assertXpathEvaluatesTo(title, &quot;/book/title&quot;, xml); this.xmlTestCase.assertXpathEvaluatesTo(author, &quot;/book/author&quot;, xml); // verify mock EasyMock.verify(this.book); }
If you appreciated Unit Testing... ...you will want to try also: Test Driven Development (TDD) Continous Integration Test Coverage tools ..
Test coverage with Cobertura
Open discussion
Some references 1/2 Wikipedia:   http://en.wikipedia.org/wiki/Unit_testing http://en.wikipedia.org/wiki/Mock_object   “ A Set of Unit Testing Rules” by Michael Feathers: http://www.artima.com/weblogs/viewpost.jsp?thread=126923 “ Extreme Programing Explained / Installed” by Kent Beck / Ron Jeffries “ Refactoring” by Martin Fowler “ JUnit Test Infected: Programmers Love Writing Tests”: http://junit.sourceforge.net/doc/testinfected/testing.htm   JUnit 4.x Cookbok:  http://junit.sourceforge.net/doc/cookbook/cookbook.htm
Some references 2/2 “ Endo-Testing: Unit Testing with Mock Objects”: www.connextra.com/aboutUs/mockobjects.pdf   EasyMock 2.2 Documentation: http://easymock.org/EasyMock2_2_Documentation.html   http://easymock.org/EasyMock2_2_ClassExtension _Documentation.html  “ Partial Mocks with EasyMock”: http://www.jroller.com/page/alessiopace?entry=partial_mocks_with_easymock “ Making more objects mockable with ASM Definalizer” http://sixlegs.com/blog/java/definalizer.html XMLUnit: http://xmlunit.sourceforge.net/

More Related Content

Embrace Unit Testing

  • 1. JUG Roma 26 th September 2006 “ Embrace Unit Testing” Alessio Pace alessio.pace [AT] gmail.com http://www.jroller.com/page/alessiopace
  • 2. 3 things about me.. Master Degree in Computer Engineering Java fan Just “Puccio” for the friends
  • 3. About this presentation What you will find: definition and survey of the benefits of Unit Testing reusing material from the Web and books (see references at the end) a little coding example to get the feeling of Unit Testing using JUnit, EasyMock and XMLUnit What you won't find: an in-depth explanation about the internals of the above testing frameworks a Test Driven example
  • 4. Agenda Introduction: The programmer life without Unit Testing What is and what is not Unit Testing The benefits of Unit Testing Bad rumours about Unit Testing, and the answers to them Practical example in Java: JUnit + EasyMock JUnit + EasyMock + XMLUnit
  • 6. Programmer life without Unit Testing Write a lot of code...and someday something doesn't work / stops working Just fixing a bug is easy, but finding it can be a nightmare: hours in front of the debugger interact like a monkey with the browser or reading the output on console Fixing it could break other parts The bug could appear again later
  • 7. What is Unit Testing A unit test is an automated and self-checked procedure used to validate that a particular module of source code works correctly a module: a single Java class The procedure is to write tests for all the methods or functions of the module The class under test should be tested in isolation
  • 8. “A set of Unit Testing rules” A test is not a unit test if: It talks to the database It communicates across the network It touches the file system It can't run at the same time as any of your other unit tests You have to do special things to your environment (such as editing config files) to run it You are not testing a class in isolation from other concrete classes
  • 9. The benefits of Unit Testing (1) Isolate each part of the program and check that the individual parts work Strict, written contract that the piece of code must satisfy Allows to refactor code and make sure it still works correctly ( regression testing )
  • 10. The benefits of Unit Testing (2) Simplifies integration : testing the parts of a program first and then testing their integration Provides documentation : clients and other developers can look at the unit tests to gain a basic understanding of the APIs and determine how to use the module to fit their needs Improves code quality : code that cannot be easily tested is not factored properly
  • 11. The benefits of Unit Testing (3) Separation of interface from implementation : a class may have some collaborator classes, testing that class can frequently lead into testing the other classes.. unit test should never go outside of its own class boundary: abstract an interface around the collaborator class, and implements that interface with a mock object (see later). This results in loosely coupled code, minimizing dependencies in the system
  • 12. Bad rumours about Unit Testing “ It requires time, and I am always overscheduled” “ It is a waste of time: if I change my classes, I have to rewrite the tests!” “ My code is rock solid, I don't need tests!”
  • 13. Answers to the bad rumours (1/2) The “I'm in hurry” issue leads to a vicious cycle: more pressure you feel -> less tests you write less tests you write -> less productive you are and the less stable your code becomes the less productive and accurate you are -> more pressure you feel
  • 14. Answers to the bad rumours (2/2) Unit Testing speeds up your development : automated and self-checked code reduces the time to manually debug your code writing unit tests immediately after you code new features (or even better, before ) lets you find bugs sooner and isolated (easy to correct) Unit Testing lets you refactor safely: a change in code that causes a regression is immediately found
  • 15. A short parenthesis on Refactoring Refactoring , a definition: “ A change made to the internal structure of software to make it easier to understand and cheaper to modify without changing its observable behaviour” (“Refactoring”, Martin Fowler) Unit Testing and Refactoring go together: before refactoring a section of code you better have solid tests that cover it test, small change, test, small change, test, ..
  • 17. Let's get into a coding example! A practical example showing the very basics of: JUnit 4.x + EasyMock 2.2 JUnit 4.x + EasyMock 2.2 + XMLUnit See the References at the end for more tutorials and explanations about the internals of the frameworks
  • 18. Our example: a high level description Suppose we are already given a IBookService interface, which defines a method to retrieve an IBook by its code Suppose also we are already given an implementation of it: RemoteBookService We want to implement and test: a trivial CachedIBookService implementation, which decorates an IBookService by caching subsequent calls for the same book code value
  • 19. Our example: UML Class Diagram We have to implement and test the CachedBookService class
  • 20. Constructor implementation of CachedBookService public class CachedBookService implements IBookService { private IBookService remoteBookService ; public CachedBookService(IBookService bookService){ if(bookService == null){ throw new IllegalArgumentException(); } this.remoteBookService = bookService; } public IBookService getRemoteBookService() { return remoteBookService; } public IBook getBook(String code) throws NoSuchElementException { // implementation left out for the moment throw new NotImplementedException(); } } Ok, let's test the constructor
  • 21. How to automatically test the constructor? We must check that: when passing a valid argument the object state after the construction is correct (the instance variable remoteBookService is set) when passing a null argument an IllegalArgumentException is thrown We will use a testing framework: JUnit
  • 22. JUnit: description JUnit is just a general testing framework which provides assertion methods You can use it to do unit testing : testing a single class in isolation, eventually using EasyMock (or other mock objects frameworks) to mock collaborators classes You can use it to do integration testing : testing the interactions of a class with real collaborator classes Almost a de-facto standard in the Java field (but have a look also at TestNG ..)
  • 23. Writing a JUnit 4.x test class A test class is just a Java class Test methods must be annotated with @Test Two configuration levels: before/after each test method call ( @Before / @After annotated methods) before/after the test class is processed ( @BeforeClass / @AfterClass annotated methods) Launch the test class from an IDE (“run as JUnit Test”) or the command line
  • 24. Testing object state after constructor import static org.junit.Assert.*; import org.junit.Test; public class CachedBookServiceTest { @Test public void testConstructor () { // anonymous class to test in isolation CachedBookService constructor IBookService bookServiceParameter = new IBookService(){ public IBook getBook(String code) throws NoSuchElementException { return null; } }; CachedBookService cachedBookService = new CachedBookService(bookServiceParameter); assertNotNull (&quot;Assert not null&quot;, cachedBookService.getRemoteBookService()); /* 3 identical ways to test the reference value: */ assertTrue (&quot;Assert same reference&quot;, bookServiceParameter == cachedBookService.getRemoteBookService()); // assertSame (&quot;Assert same reference&quot;, bookServiceParameter, cachedBookService.getRemoteBookService()); // assertEquals (“Assert same reference”, bookServiceParameter, cachedBookService.getRemoteBookService()) }
  • 25. Testing exception is thrown // CachedBookServiceTest.java @Test(expected=IllegalArgumentException.class) public void testConstructorWithNullArg () { // should throw IllegaArgumentException with null arg new CachedBookService( null ); } }
  • 26. Enjoy the green bar :-)
  • 27. Now let's implement getBook() method public class CachedBookService implements IBookService { private IbookService remoteBookService; private Map<String, IBook> cache; public CachedBookService(IBookService bookService){ if(bookService == null){ throw new IllegalArgumentException(); } this.remoteBookService = bookService; this.cache = new HashMap<String, IBook>(); } public IBook getBook(String code) throws NoSuchElementException { IBook ibook = cache.get(code); if(ibook != null){ return ibook; }else{ IBook result = this.remoteBookService.getBook(code); this.cache.put(code, result); return result; } } Ok, let's test getBook()
  • 28. How to test getBook()? The class must be tested in isolation: we don't want to rely on a concrete IBookService collaborator object We must test the interactions of the class under test with the collaborator object: first request for an IBook code must be delegated to the remoteBookService following requests for the same IBook code must be served from the cache We are going to use JUnit with a Mock Objects framework: EasyMock
  • 29. What are Mock Objects? Most software units do not work alone, but collaborate with other units To test a unit in isolation , the collaborators must be simulated Mock objects have the same interfaces of the objects they mimic a mock is configured to simulate the object behaviour that it replaces In contrast to a stub , a mock object also verifies whether it is used as expected
  • 30. Why are Mocks useful? Isolate bugs Depend on an interface and not on an implementation (which couldn't exist yet) Endo testing : not just testing the output of a method, but testing the interactions of a unit with the collaborator object(s) also called “interaction based testing” (as opposed to “state based testing” )
  • 31. EasyMock 2.2 EasyMock 2.2, two libraries: one to generate mocks for interfaces only ( easymock ) one to generate mocks for non-final concrete classes and interfaces ( easymock-classextension ) Benefits: hand writing mocks is not needed supports compiler check and refactoring supports method return values and exceptions supports checking the order and count of method calls ...
  • 32. How to get a mock object with EasyMock 2.2 create a Mock Object for the interface to simulate record the expected behavior switch the mock object to replay state perform the method call to the class under test that is supposed to interact with the mock obejct verify the mock has been used correctly
  • 33. Testing getBook() - 1 st test method @Test public void testGetBook () { // (1) create the mock for the collaborating object IBookService mockBookService = EasyMock.createMock(IBookService.class); // a mock with no behaviour as return value of getBook() IBook mockBook = EasyMock.createMock(IBook.class); // (2) record the expected behaviour EasyMock.expect(mockBookService.getBook(&quot;id123&quot;)).andReturn(mockBook); // (3) switch the mock to replay state EasyMock.replay(mockBookService); CachedBookService cachedBookService = new CachedBookService(mockBookService); // (4) perform the call to the class under test which interacts with the mock IBook result = cachedBookService.getBook(&quot;id123&quot;); assertSame(“Assert same reference”, mockBook, result); // (5) verify the mock has been used as expected EasyMock.verify(mockBookService); }
  • 34. Testing getBook() - 2 nd test method @Test public void testGetBookTwoDifferentCodes () { // (1) create the mock for the collaborating object IBookService mockBookService = EasyMock.createMock(IBookService.class); // a mock with no behaviour as return value of getBook() IBook mockBook = EasyMock.createMock(IBook.class); IBook mockBook2 = EasyMock.createMock(IBook.class); // (2) record two expected calls EasyMock.expect(mockBookService.getBook(&quot;id123&quot;)).andReturn(mockBook); EasyMock.expect(mockBookService.getBook(&quot;id456&quot;)).andReturn(mockBook2); // (3) switch the mock to replay state EasyMock.replay(mockBookService); CachedBookService cachedBookService = new CachedBookService(mockBookService); // (4) perform the call to the class under test which interacts with the mock IBook result = cachedBookService.getBook(&quot;id123&quot;); assertSame(“Assert same reference”, mockBook, result); IBook result2 = cachedBookService.getBook(&quot;id456&quot;); assertSame(“Assert same reference”, mockBook2, result2); // (5) verify the mock has been used as expected EasyMock.verify(mockBookService); }
  • 35. Testing getBook() - 3 rd test method @Test public void testGetSameBookTwoTimes () { IBookService mockBookService = EasyMock.createMock(IBookService.class); IBook mockBook = EasyMock.createMock(IBook.class); // record only one call EasyMock.expect(mockBookService.getBook(&quot;id123&quot;)).andReturn(mockBook); EasyMock.replay(mockBookService); CachedBookService cachedBookService = new CachedBookService(mockBookService); IBook result = cachedBookService.getBook(&quot;id123&quot;); assertSame(mockBook, result); IBook secondResult = cachedBookService.getBook(&quot;id123&quot;); assertSame(result, secondResult); EasyMock.verify(mockBookService); }
  • 36. Adding an IBookXmlSerializer We are already given an IBookXmlSerializer interface to serialize an IBook to XML We have to implement and test a simple IBookXmlSerializerImpl which writes an XML representation of an IBook
  • 38. IBookXmlSerializerImpl public class IBookXmlSerializerImpl implements IBookXmlSerializer { public String serialize(IBook book) { StringBuilder sb = new StringBuilder(&quot;&quot;); sb.append(&quot;<book>&quot;); sb.append(&quot; <code>&quot; + book.getCode() + &quot;</code>&quot;); sb.append(&quot; <title>&quot; + book.getTitle() + &quot;</title>&quot;); sb.append(&quot; <author>&quot; + book.getAuthor() + &quot;</author>&quot;); sb.append(&quot;</book>&quot;); return sb.toString(); } } Ok, let's test serialize()
  • 39. How to test serialize()? We must check that the XML output is correct We must test the class in isolation: we can't rely on a concrete IBook implementation (we don't have it..) We are going to use JUnit, EasyMock and XMLUnit
  • 40. Testing XML with XMLUnit “ XMLUnit extends JUnit to simplify unit testing of XML” “ It compares a control XML document to a test document or the result of a transformation, validates documents against a DTD, and compares the results of XPath expressions”
  • 41. IbookXmlSerializerTest - Part 1 import org.custommonkey.xmlunit.XMLTestCase; public class IBookXmlSerializerImplTest { private IBookXmlSerializer serializer; private IBook book; private XMLTestCase xmlTestCase; @Before public void setUp() throws Exception { this.serializer = new IBookXmlSerializerImpl(); this.book = EasyMock.createMock(IBook.class); this.xmlTestCase = new XMLTestCase(); } // .... The method annotated with @Before is executed before every @Test method execution
  • 42. IbookXmlSerializerTest - Part 2 @Test public void testSerialize() throws Exception { final String code = &quot;id1&quot;; EasyMock.expect(this.book.getCode()).andReturn(code); final String title = &quot;Unit Testing&quot;; EasyMock.expect(this.book.getTitle()).andReturn(title); final String author = &quot;Mario Rossi&quot;; EasyMock.expect(this.book.getAuthor()).andReturn(author); // switch to replay state EasyMock.replay(this.book); String xml = this.serializer.serialize(book); this.xmlTestCase.assertXpathExists(&quot;/book&quot;, xml); this.xmlTestCase.assertXpathEvaluatesTo(code, &quot;/book/code&quot;, xml); this.xmlTestCase.assertXpathEvaluatesTo(title, &quot;/book/title&quot;, xml); this.xmlTestCase.assertXpathEvaluatesTo(author, &quot;/book/author&quot;, xml); // verify mock EasyMock.verify(this.book); }
  • 43. If you appreciated Unit Testing... ...you will want to try also: Test Driven Development (TDD) Continous Integration Test Coverage tools ..
  • 44. Test coverage with Cobertura
  • 46. Some references 1/2 Wikipedia: http://en.wikipedia.org/wiki/Unit_testing http://en.wikipedia.org/wiki/Mock_object “ A Set of Unit Testing Rules” by Michael Feathers: http://www.artima.com/weblogs/viewpost.jsp?thread=126923 “ Extreme Programing Explained / Installed” by Kent Beck / Ron Jeffries “ Refactoring” by Martin Fowler “ JUnit Test Infected: Programmers Love Writing Tests”: http://junit.sourceforge.net/doc/testinfected/testing.htm JUnit 4.x Cookbok: http://junit.sourceforge.net/doc/cookbook/cookbook.htm
  • 47. Some references 2/2 “ Endo-Testing: Unit Testing with Mock Objects”: www.connextra.com/aboutUs/mockobjects.pdf EasyMock 2.2 Documentation: http://easymock.org/EasyMock2_2_Documentation.html http://easymock.org/EasyMock2_2_ClassExtension _Documentation.html “ Partial Mocks with EasyMock”: http://www.jroller.com/page/alessiopace?entry=partial_mocks_with_easymock “ Making more objects mockable with ASM Definalizer” http://sixlegs.com/blog/java/definalizer.html XMLUnit: http://xmlunit.sourceforge.net/