Successfully reported this slideshow.
Your SlideShare is downloading. ×

Unit testing: unitils & dbmaintain

Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Upcoming SlideShare
DbSetup
DbSetup
Loading in …3
×

Check these out next

1 of 81 Ad
Advertisement

More Related Content

Slideshows for you (20)

Similar to Unit testing: unitils & dbmaintain (20)

Advertisement

Recently uploaded (20)

Unit testing: unitils & dbmaintain

  1. 1. Unit testing unitils - dbmaintain Filip Neven
  2. 2. Presentation goal Demonstrate the added value Unitils can give to your projects
  3. 3. Unitils ‘Mission statement’ <ul><li>Provide a good testing environment
  4. 4. Remove boilerplate code
  5. 5. Automate stuff where possible
  6. 6. Help applying best practices </li></ul>
  7. 7. Why Unitils? <ul><li>Tackle complexity of persistence layer testing </li></ul><ul><ul><li>Many practical problems
  8. 8. A lot of boilerplate code required </li></ul></ul><ul><li>Offer all kinds of useful functionalities </li></ul><ul><ul><li>Assertion using reflection
  9. 9. Mock objects </li></ul></ul>
  10. 10. Agenda <ul><li>History
  11. 11. Features by example </li></ul><ul><ul><li>Persistence layer testing
  12. 12. Reflection assert
  13. 13. Database maintenance
  14. 14. Mock objects </li></ul></ul><ul><li>Unitils design
  15. 15. Roadmap & summary </li></ul>
  16. 16. Introduction <ul><li>Originates from Ordina Testing & QA task force (2005) </li></ul><ul><ul><li>Brainstorm
  17. 17. Testing guidelines: www.unitils.org/guidelines.html
  18. 18. Supporting code </li></ul></ul> Unitils open source project
  19. 19. Features <ul><li>Persistence layer testing </li></ul><ul><ul><li>JPA
  20. 20. Hibernate </li></ul></ul><ul><li>Automatic database maintenance </li></ul><ul><ul><li>DbMaintain </li></ul></ul><ul><li>General testing utilities </li></ul><ul><ul><li>Reflection assert </li></ul></ul><ul><li>Spring integration
  21. 21. Mock objects </li></ul>
  22. 22. Persistence layer testing <ul><li>Important to test persistence layer </li></ul><ul><ul><li>Queries are often complex
  23. 23. Two separated parts -> mismatches more likely </li></ul></ul><ul><li>Testing persistence is difficult </li></ul><ul><ul><li>Test database
  24. 24. Provide test data </li></ul></ul><ul><li>Lot of boilerplate code required </li></ul><ul><ul><li>Depends on technology: Plain old JDBC, ORM, spring </li></ul></ul>
  25. 25. Persistence layer testing guidelines <ul><li>Need control over database & test data </li></ul><ul><ul><li>Test database
  26. 26. Empty database
  27. 27. Small dataset per group of related tests </li></ul></ul><ul><ul><ul><li>Typically one dataset per test class </li></ul></ul></ul>
  28. 28. Separate developer schema’s <ul><li>Separate schema per developer </li></ul><ul><ul><li>Develop in isolation
  29. 29. Test in isolation </li></ul></ul>
  30. 30. Persistence layer testing – DAO example public class UserDao { @PersistenceContext private EntityManager entityManager; public List<User> findByLastName(String lastName) { return entityManager.createQuery( “ select u from User u where u.lastName = :lastName”) .setParameter(“lastName”, lastName) .getResultList(); } // ... }
  31. 31. Persistence layer testing – test example @DataSet @JpaEntityManagerFactory (persistenceUnit=“eshop”, configFiles={“persistence-test.xml”}) public class UserDaoTest extends UnitilsJUnit4 { @PersistenceContext EntityManager entityManager; UserDao userDao; @Before public void init() { JpaUnitils.injectEntityManagerInto (userDao); } @Test public void testFindByLastName() { List<User> users = userDao.findByLastName(&quot;Doe&quot;); assertPropertyLenientEquals (&quot;userName&quot;, Arrays.asList(&quot;johnDoe&quot;, &quot;janeDoe&quot;), users); } }
  32. 32. Loading test data - example @DataSet @JpaEntityManagerFactory(persistenceUnit=“eshop”, configFiles={“persistence-test.xml”}) public class UserDaoTest extends UnitilsJUnit4 { @PersistenceContext EntityManager entityManager; UserDao userDao; @Before public void init() { JpaUnitils.injectEntityManagerInto(userDao); } @Test public void testFindByLastName() { List<User> users = userDao.findByLastName(&quot;Doe&quot;); assertPropertyLenientEquals(&quot;userName&quot;, Arrays.asList(&quot;johnDoe&quot;, &quot;janeDoe&quot;), users); } }
  33. 33. Loading test data <ul><li>@DataSet </li></ul><ul><ul><li>Dbunit dataset is loaded before each test </li></ul></ul><ul><ul><ul><li>Tables are cleared first </li></ul></ul></ul><ul><ul><li>Default file: same package & name as class </li></ul></ul>UserDaoTest.xml: <?xml version='1.0' encoding='UTF-8'?> <dataset [… XSD declaration …]> <user id=&quot;1&quot; userName=&quot;johnDoe&quot; /> <user id=&quot;2&quot; userName=&quot;janeRoe&quot; /> <user id=&quot;3&quot; userName=&quot;janeDoe&quot; /> </dataset>  Put database in known state before each test
  34. 34. Loading test data - possibilities <ul><li>Custom file name </li></ul><ul><li>Method - specific data set </li></ul><ul><li>Load multiple datasets </li></ul>@DataSet(&quot;UserData.xml&quot;) public class UserDaoTest extends UnitilsJUnit4 { @DataSet(&quot;UserData-adminUser.xml&quot;) public void testFindAdminUsers() { @DataSet({&quot;ReferenceData.xml&quot;, &quot;UserData.xml&quot;}) public class UserDaoTest extends UnitilsJUnit4 {
  35. 35. Loading test data - XSD <ul><li>XSD for validation & auto completion </li></ul><ul><ul><li>Automatically generated by Unitils </li></ul></ul><?xml version='1.0' encoding='UTF-8'?> <dataset xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema- instance&quot; xsi:noNamespaceSchemaLocation=&quot;<path to dataset.xsd file>&quot; > <user id=&quot;1&quot; userName=&quot;johnDoe&quot; age=&quot;54&quot;/> <user id=&quot;2&quot; userName=&quot;janeRoe&quot; lastName=&quot;Roe&quot;/> <user id=&quot;3&quot; userName=&quot;janeDoe&quot; firstName=&quot;Jane&quot;/> </dataset>
  36. 36. Loading test data – multi-schema support <ul><li>Multi – schema dataset support </li></ul><?xml version='1.0' encoding='UTF-8'?> <dataset xmlns:schema1=&quot;SCHEMA1&quot; xmlns:schema2=&quot;SCHEMA2&quot; > < schema1 :user id=&quot;1&quot; userName=&quot;johnDoe&quot; age=&quot;54&quot;/> < schema2 :user id=&quot;2&quot; userName=&quot;janeRoe&quot; lastName=&quot;Roe&quot;/> </dataset>
  37. 37. Loading test data – dbunit issues <ul><li>Solve annoying dbunit issue </li></ul>With Unitils: <?xml version='1.0' encoding='UTF-8'?> <dataset [… namespace declaration …]> <user id=&quot;1&quot; userName=&quot;johnDoe&quot; lastName=&quot;Roe&quot;/> <user id=&quot;2&quot; userName=&quot;janeRoe&quot;/> </dataset> With plain dbunit: <?xml version='1.0' encoding='UTF-8'?> <dataset [… namespace declaration …]> <user id=&quot;1&quot; userName=&quot;johnDoe&quot; lastName=&quot;Roe&quot; /> <user id=&quot;2&quot; userName=&quot;janeRoe&quot; lastName=“[null]&quot;/> </dataset> When using dbunit directly, this is not possible: Once you’ve used a column, you must use it in every record
  38. 38. Verify database contents <ul><li>@ExpectedDataSet </li></ul><ul><ul><li>Verify database contents after test execution </li></ul></ul>UserDaoTest.testCreateNewUser-expected.xml: <?xml version='1.0' encoding='UTF-8'?> <dataset [… namespace declaration …]> <user id=&quot;1&quot; userName=&quot;johnDoe&quot;/> </dataset> @ExpectedDataSet public void testCreateNewUser() { // ... }
  39. 39. Transaction support <ul><li>Run each test in a transaction </li></ul><ul><ul><li>Avoid problems with ‘select for update’ or deferred check constraints
  40. 40. Required in some environments </li></ul></ul><ul><li>Possible to rollback after each test </li></ul><ul><ul><li>Facilitates test isolation: DB stays in original state
  41. 41. Enables using pre-filled test DB </li></ul></ul><ul><li>Default behavior: commit after every test </li></ul><ul><ul><li>Modify using @Transactional </li></ul></ul><ul><li>Powered by spring </li></ul>
  42. 42. Transaction support - example @DataSet @Transactional(TransactionMode.ROLLBACK) @JpaEntityManagerFactory(persistenceUnit=“eshop”, configFiles={“persistence-test.xml”}) public class UserDaoTest extends UnitilsJUnit4 { @PersistenceContext EntityManager entityManager; UserDao userDao; @Before public void init() { JpaUnitils.injectEntityManagerInto(userDao); } @Test public void testFindByLastName() { List<User> users = userDao.findByLastName(&quot;Doe&quot;); assertPropertyLenientEquals(&quot;userName&quot;, Arrays.asList(&quot;johnDoe&quot;, &quot;janeDoe&quot;), users); } }
  43. 43. JPA integration - example @DataSet @JpaEntityManagerFactory(persistenceUnit=“eshop”, configFiles={“persistence-test.xml”}) public class UserDaoTest extends UnitilsJUnit4 { @PersistenceContext EntityManager entityManager; UserDao userDao; @Before public void init() { JpaUnitils.injectEntityManagerInto (userDao); } @Test public void testFindByLastName() { List<User> users = userDao.findByLastName(&quot;Doe&quot;); assertPropertyLenientEquals(&quot;userName&quot;, Arrays.asList(&quot;johnDoe&quot;, &quot;janeDoe&quot;), users); } }
  44. 44. JPA integration <ul><li>@JpaEntityManagerFactory </li></ul><ul><ul><li>Create EntityManagerFactory
  45. 45. Connects with unit test database
  46. 46. New EntityManager for each test </li></ul></ul><ul><ul><ul><li>Attached to unitils transaction </li></ul></ul></ul><ul><ul><li>Powered by spring </li></ul></ul>
  47. 47. JPA integration – persistence providers <ul><li>Supported persistence providers </li></ul><ul><ul><li>Hibernate
  48. 48. Toplink
  49. 49. OpenJPA </li></ul></ul>
  50. 50. JPA integration – mapping test <ul><li>Entity / database mapping test </li></ul><ul><ul><li>Verifies if database structure is in sync with entities
  51. 51. Only works when persistence provider = hibernate </li></ul></ul>@Test public void testMappingToDatabase() { JpaUnitils.assertMappingWithDatabaseConsistent(); } Found mismatches between Java objects and database tables. Applying DDL statements should resolve the problem: alter table PERSON add column lastName varchar(255); alter table PRODUCT add column barCode varchar(255);
  52. 52. Reflection assert - example @DataSet @JpaEntityManagerFactory(persistenceUnit=“eshop”, configFiles={“persistence-test.xml”}) public class UserDaoTest extends UnitilsJUnit4 { @PersistenceContext EntityManager entityManager; UserDao userDao; @Before public void init() { JpaUnitils.injectEntityManagerInto(userDao); } @Test public void testFindByLastName() { List<User> users = userDao.findByLastName(&quot;Doe&quot;); assertPropertyLenientEquals (&quot;userName&quot;, Arrays.asList(&quot;johnDoe&quot;, &quot;janeDoe&quot;), users); } }
  53. 53. Reflection assert <ul><li>ReflectionAssert </li></ul>assertReflectionEquals(expectedUser, actualUser); <ul><ul><li>Compares all fields using reflection
  54. 54. Recursively compares inner objects
  55. 55. Loops over collections and arrays
  56. 56. Reports all differences </li></ul></ul>
  57. 57. Reflection assert - leniency <ul><li>Leniency </li></ul><ul><ul><li>Keep your tests maintainable
  58. 58. Only verify what’s relevant for the test </li></ul></ul><ul><li>Lenient by default: </li></ul><ul><ul><li>Lenient number types
  59. 59. Lenient collection types </li></ul></ul><ul><li>Leniency options: assertLenientEquals ( ... ) </li></ul><ul><ul><li>Lenient order of collections
  60. 60. Ignore java default values (0 or null) </li></ul></ul>
  61. 61. Reflection assert – lenient collection order <ul><li>Ignore order of collections and arrays </li></ul>assertReflectionEquals(expectedCollection, actualCollection, ReflectionComparatorMode.LENIENT_ORDER); assertLenientEquals(expectedCollection, actualCollection); Expected users: [ john , jane ] Actual users: [ jane , john ]
  62. 62. Reflection assert – ignore defaults <ul><li>Ignore java default values: 0 or null </li></ul>assertReflectionEquals(expectedCollection, actualCollection, ReflectionComparatorMode.IGNORE_DEFAULTS); assertLenientEquals(expectedUser, actualUser); Expected id: 0 first name: Jane last name: null Actual id: 123 first name: Jane last name: Doe
  63. 63. Reflection assert – check property value <ul><li>Check property value </li></ul>assertPropertyLenientEquals( &quot; address.houseNr &quot; , 5 , user); <ul><li>Check property value for all elements in collection </li></ul>assertPropertyLenientEquals( “ userName &quot; , Arrays.asList( &quot; johnDoe &quot; , &quot; janeDoe &quot; ), users);
  64. 64. Persistence layer testing - example @DataSet @JpaEntityManagerFactory (persistenceUnit=“eshop”, configFiles={“persistence-test.xml”}) public class UserDaoTest extends UnitilsJUnit4 { @PersistenceContext EntityManager entityManager; UserDao userDao; @Before public void init() { JpaUnitils.injectEntityManagerInto (userDao); } @Test public void testFindByLastName() { List<User> users = userDao.findByLastName(&quot;Doe&quot;); assertPropertyLenientEquals (&quot;userName&quot;, Arrays.asList(&quot;johnDoe&quot;, &quot;janeDoe&quot;), users); } }
  65. 65. Base test class with all plumbing @DataSet @JpaEntityManagerFactory (persistenceUnit=“eshop”, configFiles={“persistence-test.xml”}) public class BaseDaoTest extends UnitilsJUnit4 { @PersistenceContext protected EntityManager entityManager; protected Dao dao = createDao(); @Before public void init() { JpaUnitils.injectEntityManagerInto (dao); } protected abstract DAO createDAO(); }
  66. 66. Test without plumbing public class UserDaoTest extends BaseDaoTest { UserDao userDao = new UserDao(); @Override protected Object getTestedObject() { return userDao; } @Test public void testFindByLastName() { List<User> users = dao.findByLastName(&quot;Doe&quot;); assertPropertyLenientEquals(&quot;userName&quot;, Arrays.asList(&quot;johnDoe&quot;, &quot;janeDoe&quot;), users); } }
  67. 67. Hibernate support - example @DataSet public class UserDaoTest extends UnitilsJUnit4 { @HibernateSessionFactory(“hibernate-test.cfg.xml”) EntityManagerFactory entityManagerFactory; UserDao userDao; @Before // Instantiate UserDao and inject EntityManagerFactory @Test public void testFindByLastName() { List<User> users = userDao.findByLastName(&quot;Doe&quot;); assertPropertyLenientEquals(&quot;userName&quot;, Arrays.asList(&quot;johnDoe&quot;, &quot;janeDoe&quot;), users); } }
  68. 68. Spring integration – DAO example import org.springframework.orm.hibernate3.support.HibernateDaoSupport; public class UserDao extends HibernateDaoSupport { // ... public List<User> findByLastName(String lastName) { return (Long) getHibernateTemplate().findByNamedParam( &quot;from User u where u.lastName = :lastName&quot;, &quot;user&quot;, user).get(0); } // ... }
  69. 69. Spring integration – test example @SpringApplicationContext({“eshop-config.xml”, “test-config.xml”}) @DataSet public class UserDaoTest extends UnitilsJUnit4 { @SpringBean(“userDao”) UserDao userDao; @Test public void testFindByLastName() { List<User> users = userDao.findByLastName(&quot;Doe&quot;); assertPropertyLenientEquals(&quot;userName&quot;, Arrays.asList(&quot;johnDoe&quot;, &quot;janeDoe&quot;), users); } }
  70. 70. Spring integration - configuration eshop-config.xml <bean id=&quot;userDao&quot; class=&quot;eshop.dao.UserDao&quot;> <property name=&quot;sessionFactory&quot; ref=&quot;sessionFactory&quot;/> </bean> <bean id=&quot;sessionFactory&quot; class=&quot;..AnnotationSessionFactoryBean&quot;> <property name=&quot;dataSource&quot; ref=&quot;dataSource&quot;/> <property name=&quot;annotatedClasses&quot;> <list> <value>eshop.model.User</value> </list> </property> </bean> <bean id=&quot;dataSource&quot;> … </bean> test-config.xml <bean id=&quot;dataSource&quot; class=&quot;org.unitils..UnitilsDataSourceFactoryBean&quot;/>
  71. 71. Spring integration <ul><li>@SpringApplicationContext </li></ul><ul><ul><li>Application context is loaded
  72. 72. Typically application & test specific config file </li></ul></ul><ul><li>@SpringBean </li></ul><ul><ul><li>Inject bean from ApplicationContext into test
  73. 73. Other possibilities </li></ul></ul><ul><ul><ul><li>@SpringBeanByName, @SpringBeanByType </li></ul></ul></ul>
  74. 74. Automatic database maintenance <ul><li>Automatic database maintenance </li></ul><ul><ul><li>Currently still part of unitils (2.2)
  75. 75. Split off into DbMaintain project </li></ul></ul><ul><ul><ul><li>1.0 released in february </li></ul></ul></ul><ul><ul><li>Will be removed from Unitils </li></ul></ul>
  76. 76. Why automatic database maintenance? <ul><li>Drawbacks of manual maintenance </li></ul><ul><ul><li>Lot of databases – lot to maintain
  77. 77. What was rolled out where? </li></ul></ul> Time consuming, error prone
  78. 78. Why automatic database maintenance? <ul><li>Fully automatic deployments </li></ul><ul><ul><li>Enables frequent deployments
  79. 79. Tester / customer can trigger deployment </li></ul></ul><ul><ul><ul><li>Shorter feedback cycle </li></ul></ul></ul><ul><ul><li>Enables automatic integration tests </li></ul></ul>
  80. 80. Database maintainer – basic usage <ul><ul><li>Folder with indexed scripts </li></ul></ul><ul><ul><li>Database table stores executed scripts info </li></ul></ul><ul><ul><li>Rollout: Only apply new scripts </li></ul></ul>FILE_NAME VERSION LAST_MODIFIED CHECKSUM EXECUTED_AT SUCCEEDED 01_users.sql 1 1206695947921 15a4be468g 2008-09-17 20:23:12 1 02_roles.sql 1 1206695947996 79a5b32g10 2008-09-18 10:15:12 1 DBMAINTAIN_SCRIPTS dbscripts / 01_users.sql / 02_roles.sql
  81. 81. Database maintainer – script organization <ul><li>Work incremental </li></ul><ul><ul><li>Database change = new script
  82. 82. What’s committed cannot be changed
  83. 83. Sequence is strict </li></ul></ul>dbscripts / incremental / 01_initial / 01_users.sql / 02_sprint1 / 01_roles.sql 02_groups.sql
  84. 84. Database maintainer – script organization <ul><li>Hierarchic organization </li></ul><ul><ul><li>Organize per release, sprint </li></ul></ul>dbscripts / incremental / 01_initial / 01_users.sql / 02_sprint1 / 01_roles.sql 02_groups.sql
  85. 85. Database maintainer – script organization <ul><li>Incremental / repeatable scripts </li></ul><ul><ul><li>Indexed = incremental
  86. 86. Non-indexed = repeatable </li></ul></ul><ul><ul><li>Update incremental script  error
  87. 87. Update repeatable script  re-execute </li></ul></ul>dbscripts / incremental / 01_initial / 01_users.sql / 02_sprint1 / 01_roles.sql 02_groups.sql repeatable / view_users_groups.sql
  88. 88. Database maintainer – post processing <ul><li>Post-processing scripts </li></ul>dbscripts / incremental / 01_initial / 01_users.sql / 02_sprint1 / 01_roles.sql 02_groups.sql repeatable / view_users_groups.sql postprocessing / compile_all.sql create_grants.sql
  89. 89. Database maintainer – multi-db <ul><li>Multi database / db user support </li></ul><ul><ul><li>Define different datasources </li></ul></ul><ul><ul><ul><li>Usually: same DB – different DB user </li></ul></ul></ul><ul><ul><li>Indicate target DB in script name </li></ul></ul><ul><ul><ul><li>Use logical name in script: linked to physical DB at execution time </li></ul></ul></ul>01_products.sql 02_ @users _usergroups.sql
  90. 90. Database maintainer – multi-db <ul><li>Support for patches </li></ul><ul><ul><li>Fix on production branch  merge into DEV </li></ul></ul> Not allowed: Not in correct sequence dbscripts / incremental / 01_v1.0 / 01_users.sql / 02_#patch_add_status.sql / 02_v1.1 / 01_roles.sql 02_groups.sql
  91. 91. Database maintainer – multi-db <ul><li>Support for renames </li></ul><ul><ul><li>Rename scripts / folders
  92. 92. Renumber script / folder indexes
  93. 93. Split scripts into folders
  94. 94. Move scripts from separate folders together </li></ul></ul> Only limitation: relative sequence scripts can't change
  95. 95. Database maintainer & the SCM <ul><li>Database setup is put into version control!
  96. 96. Atomic: Source and db modifications </li></ul><ul><ul><li>Comitted together
  97. 97. Checked out together
  98. 98. Deployed together </li></ul></ul> No temporary failures!
  99. 99. Database script jar <ul><li>Create jar file with scripts </li></ul><ul><ul><li>Self contained: Containing own configuration </li></ul></ul><ul><li>Publish scripts as a deliverable just like war or ear </li></ul>
  100. 100. Database maintainer – multi-db <ul><li>Other dbmaintain operations </li></ul><ul><ul><li>markDatabaseAsUpToDate
  101. 101. clearDatabase
  102. 102. cleanDatabase
  103. 103. disableConstraints
  104. 104. updateSequences </li></ul></ul>
  105. 105. DbMaintain execution <ul><li>Execution using ant </li></ul><ul><li>Command line </li></ul><updateDatabase scriptLocations= &quot; ... &quot; > <database url= &quot; ... &quot; ... /> </updateDatabase> > dbmaintain updateDatabase eshop-db.jar – config dbmaintain-st.properties
  106. 106. Recreation from scratch <ul><li>From scratch option </li></ul><ul><ul><li>Possible to modify indexed script  triggers from-scratch recreation
  107. 107. No data preservation
  108. 108. Perfect for unit / integration test databases </li></ul></ul>
  109. 109. Unitils integration <ul><li>Integration with unitils </li></ul><ul><ul><li>Auto-maintain unit test schema
  110. 110. Check for script updates before running tests
  111. 111. Make db more ‘test friendly’ </li></ul></ul><ul><ul><ul><li>Disable constraints
  112. 112. Update sequences </li></ul></ul></ul>
  113. 113. Disable FK & not-null constraints <ul><li>Why disable FK & not null constraints? </li></ul><ul><ul><li>What we need </li></ul></ul><ul><ul><li>What we need to write: </li></ul></ul><dataset> <user id=&quot;1&quot; username=&quot;jdoe&quot; /> </dataset> <dataset> <user id=&quot;1&quot; username=&quot;jdoe&quot; firstname=&quot;john&quot; lastname=&quot;Doe&quot; role_id=&quot;1&quot; company_id=&quot;1&quot; /> <role id=&quot;1&quot; rolename=&quot;admin&quot; /> <company id=&quot;1&quot; name=“Agit&quot; /> </dataset>
  114. 114. Database maintenance <ul><li>DB maintainer </li></ul><ul><ul><li>Automatic DB post-processing to make it more test-friendly </li></ul></ul><ul><ul><ul><li>Disable foreign key & not null constraints
  115. 115. Increase value of sequences and identity columns </li></ul></ul></ul><ul><ul><li>Generate XSD or DTD for dataset definition </li></ul></ul>
  116. 116. Mock objects support <ul><li>Brand new mock object library
  117. 117. Goals </li></ul><ul><ul><li>Create the ‘best of breed’
  118. 118. Define the simplest possible syntax
  119. 119. Promote best practices </li></ul></ul>
  120. 120. Mock objects support example public class AlertServiceTest extends UnitilsJUnit4 { Mock <Scheduler> mockScheduler; Mock <Messenger> mockMessenger; // Test data setup @Test public void testSendScheduledAlerts() { mockScheduler.returns (alerts).getScheduledAlerts(myUser)); alertService.sendScheduledAlerts(); mockMessenger.assertInvoked() .sendMessage(alert1); mockMessenger.assertInvoked() .sendMessage(alert2); } }
  121. 121. Separate behavior definition from verification <ul><li>Separate behavior definition from verification </li></ul>Compared to EasyMock: mockScheduler.returns(alerts).getScheduledAlerts(myUser); alertService.sendScheduledAlerts(); mockMessenger.assertInvoked().sendMessage(alert1); mockMessenger.assertInvoked().sendMessage(alert2); expect(mockScheduler).getScheduledAlerts(myUser) .andReturn(alerts); mockMessenger.sendMessage(alert1); mockMessenger.sendMessage(alert2); replay(); alertService.sendScheduledAlerts();
  122. 122. Behavior definition syntax <ul><li>Simple, consistent syntax </li></ul><ul><ul><li>Behavior definition
  123. 123. Invocation asserts
  124. 124. No static imports needed </li></ul></ul>Compared to EasyMock: mockObject.returns(value).getA(); mockObject.throws(exception).doSomethingInvalid(); mockObject.assertInvoked().doSomething(); expect(mockObject.getA()).andReturn(value); mockObject.doSomethingInvalid(); expectLastCall().andThrow(exception); mockObject.doSomething();
  125. 125. Behavior definition syntax <ul><li>Verify invocations: sequence not important
  126. 126. Strict sequence </li></ul>mockMessenger. assertInvoked() .sendMessage(alert1); mockMessenger. assertInvoked() .sendMessage(alert2); mockMessenger. assertInvokedInSequence() .sendMessage(alert1); mockMessenger. assertInvokedInSequence() .sendMessage(alert2);
  127. 127. Behavior definition syntax <ul><li>By default all invocations allowed </li></ul><ul><ul><li>Default return values: null, empty collection / array </li></ul></ul><ul><li>Behavior definitions match multiple times
  128. 128. To match only once: </li></ul>mockScheduler.returns(alerts).getScheduledAlerts(myUser); mockScheduler.getMock().getScheduledAlerts(); -> returns alerts mockScheduler.getMock().getScheduledAlerts(); -> returns alerts mockScheduler.onceReturns(alerts).getScheduledAlerts(myUser); mockScheduler.getMock().getScheduledAlerts(); -> returns alerts mockScheduler.getMock().getScheduledAlerts(); -> returns null
  129. 129. Relax argument constraints <ul><li>Mix argument matchers with concrete objects </li></ul><ul><li>EasyMock: Mixing not possible </li></ul>either or mockScheduler.returns(alerts) .getScheduledAlerts( notNull(Date.class), myUser ); expect(mockScheduler).getScheduledAlerts( toDate(“2008/09/18”), myUser ).andReturn(alerts)); expect(mockScheduler).getScheduledAlerts( (Date) notNull(), eq(myUser) ).andReturn(alerts));
  130. 130. Relax argument constraints <ul><li>Null means ‘I don’t care’ </li></ul><ul><li>Is same as </li></ul>mockScheduler.returns(alerts).getScheduledAlerts( null, null ); mockScheduler.returns(alerts) .getScheduledAlerts( (Date)anyObject(), (User)anyObject() );
  131. 131. Relax argument constraints <ul><li>Default argument matching: lenient reflection assert
  132. 132. Copy of objects is taken: If object changed during
  133. 133. test, object at call time is matched </li></ul>mockMessenger.assertInvoked().sendAlert(new Alert(null, null, ChannelType.EMAIL)); mockMessenger.assertInvoked().sendAlert(new Alert(null, null, ChannelType.SMS));
  134. 134. Feedback <ul><li>User feedback </li></ul><ul><ul><li>Observed scenario
  135. 135. Suggested assert statements
  136. 136. Object contents </li></ul></ul>Observed scenario: mockScheduler.getScheduledAlerts ...at mydom.AlertService:55 mockMessenger.send(alert1) ...at mydom.AlertService:76 mockMessenger.send(alert2) ...at mydom.AlertService:76 Suggested assert statements: mockMessenger.assertInvoked().send(alert1); mockMessenger.assertInvoked().send(alert2);
  137. 137. Other features <ul><li>Other features </li></ul><ul><ul><li>Use original implementation for some methods </li></ul></ul><ul><ul><li>‘ Dummy’ objects </li></ul></ul>PartialMock <Messenger> mockMessenger; @Dummy Message alert1, alert2;
  138. 138. Mock injection AlertService alertService; Mock<Scheduler> mockScheduler; Mock<Messenger> mockMessenger; @Before public void setUp() { alertService = new AlertService( mockScheduler. getMock() , mockMessenger. getMock() ); } <ul><li>‘ Manual’ injection </li></ul>
  139. 139. Mock injection @TestedObject AlertService alertService; @InjectIntoByType Mock <Scheduler> mockScheduler ; @InjectIntoByType Mock <Messenger> mockMessenger; <ul><li>Using unitils injection features </li></ul>
  140. 140. Injection <ul><li>Injection of any object into any other object </li></ul><ul><li>Tested object is default target </li></ul><ul><li>Auto-detect target property </li></ul>@InjectInto (target = “targetObject”, property = “targetProperty”) SomeClass injectedObject; TargetClass targetObject; @InjectInto (property = “targetProperty”) SomeClass injectedObject; @TestedObject TargetClass targetObject; @InjectIntoByType SomeClass byTypeInjectedObject; @InjectIntoByName OtherClass byNameInjectedObject; @TestedObject TargetClass targetObject;
  141. 141. Unitils architecture <ul><li>Test execution listener
  142. 142. Modules listening to test execution
  143. 143. Annotations used to instruct modules </li></ul>@DataSet, @SpringBean, @Transactional Test Test Listener Database Module DbUnit Module ... Module
  144. 144. Unitils architecture <ul><li>Need to extend base class </li></ul>org.unitils. UnitilsJUnit3 org.unitils. UnitilsJUnit4 org.unitils. UnitilsTestNG <ul><li>Use custom test runner (JUnit 4 only) </li></ul>@RunWith( UnitilsJUnit4TestClassRunner .class)
  145. 145. Unitils architecture <ul><li>Modules </li></ul><ul><ul><li>DatabaseModule
  146. 146. DbUnitModule
  147. 147. JpaModule
  148. 148. HibernateModule
  149. 149. SpringModule
  150. 150. MockModule
  151. 151. EasyMockModule
  152. 152. InjectModule </li></ul></ul>
  153. 153. Advantages of unitils design <ul><li>Easily extendable and customizable </li></ul><ul><ul><li>Add new modules or switch implementation
  154. 154. Disable modules that you don’t use </li></ul></ul><ul><ul><ul><li>Automatically if a dependency cannot be found </li></ul></ul></ul><ul><ul><li>Different target environments </li></ul></ul><ul><ul><ul><li>Can also be used without spring, hibernate, ... </li></ul></ul></ul><ul><li>Free choice of unit test framework </li></ul><ul><ul><li>JUnit 3, JUnit 4, TestNG </li></ul></ul>
  155. 155. Spring integration <ul><li>Spring offers similar testing framework </li></ul><ul><ul><li>Abstraction of test framework
  156. 156. Wire test with beans from app-ctx
  157. 157. Run tests in transaction </li></ul></ul><ul><li>Upcoming: integration (2.1) </li></ul><ul><ul><li>Use spring test with unitils features </li></ul></ul>
  158. 158. Spring integration @ContextConfiguration({“eshop-config.xml”, “test-config.xml”}) @DataSet public class UserDaoTest extends AbstractTransactionalUnitilsJUnit4SpringContextTests { @AutoWired UserDao userDao; @Test public void testFindByLastName() { List<User> users = userDao.findByLastName(&quot;Doe&quot;); assertPropertyLenientEquals(&quot;userName&quot;, Arrays.asList(&quot;johnDoe&quot;, &quot;janeDoe&quot;), users); } }
  159. 159. Roadmap Q3 2009 Q4 2009 DbMaintain 1.1 Unitils 2.3 Unitils 3.0 Separate modules DbMaintain 1.2 Spring test integration Multi db support Various improvements Maven integration Profiles support
  160. 160. Links <ul><li>www.unitils.org </li></ul><ul><ul><li>Project info
  161. 161. Cookbook (quick start guide)
  162. 162. Tutorial
  163. 163. Forum & issue tracker </li></ul></ul>Mail me: filip.neven@unitils.org
  164. 164. Q&A

Editor's Notes

×