Your SlideShare is downloading. ×
0
Le Tour de xUnit
Le Tour de xUnit
Le Tour de xUnit
Le Tour de xUnit
Le Tour de xUnit
Le Tour de xUnit
Le Tour de xUnit
Le Tour de xUnit
Le Tour de xUnit
Le Tour de xUnit
Le Tour de xUnit
Le Tour de xUnit
Le Tour de xUnit
Le Tour de xUnit
Le Tour de xUnit
Le Tour de xUnit
Le Tour de xUnit
Le Tour de xUnit
Le Tour de xUnit
Le Tour de xUnit
Le Tour de xUnit
Le Tour de xUnit
Le Tour de xUnit
Le Tour de xUnit
Le Tour de xUnit
Le Tour de xUnit
Le Tour de xUnit
Le Tour de xUnit
Le Tour de xUnit
Le Tour de xUnit
Le Tour de xUnit
Le Tour de xUnit
Le Tour de xUnit
Le Tour de xUnit
Le Tour de xUnit
Le Tour de xUnit
Le Tour de xUnit
Le Tour de xUnit
Le Tour de xUnit
Le Tour de xUnit
Le Tour de xUnit
Le Tour de xUnit
Le Tour de xUnit
Le Tour de xUnit
Le Tour de xUnit
Le Tour de xUnit
Le Tour de xUnit
Le Tour de xUnit
Le Tour de xUnit
Le Tour de xUnit
Le Tour de xUnit
Le Tour de xUnit
Le Tour de xUnit
Le Tour de xUnit
Le Tour de xUnit
Le Tour de xUnit
Le Tour de xUnit
Le Tour de xUnit
Le Tour de xUnit
Le Tour de xUnit
Le Tour de xUnit
Le Tour de xUnit
Le Tour de xUnit
Le Tour de xUnit
Le Tour de xUnit
Le Tour de xUnit
Le Tour de xUnit
Le Tour de xUnit
Le Tour de xUnit
Le Tour de xUnit
Le Tour de xUnit
Le Tour de xUnit
Le Tour de xUnit
Le Tour de xUnit
Le Tour de xUnit
Le Tour de xUnit
Le Tour de xUnit
Le Tour de xUnit
Le Tour de xUnit
Le Tour de xUnit
Le Tour de xUnit
Le Tour de xUnit
Le Tour de xUnit
Le Tour de xUnit
Le Tour de xUnit
Le Tour de xUnit
Le Tour de xUnit
Le Tour de xUnit
Le Tour de xUnit
Le Tour de xUnit
Le Tour de xUnit
Le Tour de xUnit
Le Tour de xUnit
Le Tour de xUnit
Le Tour de xUnit
Le Tour de xUnit
Le Tour de xUnit
Le Tour de xUnit
Le Tour de xUnit
Le Tour de xUnit
Le Tour de xUnit
Le Tour de xUnit
Le Tour de xUnit
Le Tour de xUnit
Upcoming SlideShare
Loading in...5
×

Thanks for flagging this SlideShare!

Oops! An error has occurred.

×
Saving this for later? Get the SlideShare app to save on your phone or tablet. Read anywhere, anytime – even offline.
Text the download link to your phone
Standard text messaging rates apply

Le Tour de xUnit

1,651

Published on

This session showcases several unit testing libraries and frameworks that make it possible to write the most-effective unit and integration tests. It also highlights some of the most underused …

This session showcases several unit testing libraries and frameworks that make it possible to write the most-effective unit and integration tests. It also highlights some of the most underused features of JUnit, including Theories, parameterized tests, and Hamcrest matchers.

Among the libraries it covers: Unitils, Spring Test, DbUnit, XMLUnit, Dumpster, Mockito, and JBehave. The presentation supplies code examples and discusses TDD/BDD best practices.

Published in: Technology, Education
0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total Views
1,651
On Slideshare
0
From Embeds
0
Number of Embeds
1
Actions
Shares
0
Downloads
46
Comments
0
Likes
0
Embeds 0
No embeds

Report content
Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
No notes for slide

Transcript

  • 1. Le Tour de xUnit<br />JavaOne 2011<br />AbdelmonaimRemani<br />abdelmonaim.remani@gmail.com<br />
  • 2. License<br />Creative Commons Attribution-NonCommercial3.0 Unported<br />http://creativecommons.org/licenses/by-nc/3.0/<br />
  • 3. Who Am I?<br />Software Engineer<br />Particularly interested in technology evangelism and enterprise software development and architecture<br />President and Founder of a number of organizations<br />The Chico Java User Group<br />The Silicon Valley Spring User Group<br />LinkedIn<br />http://www.linkedin.com/in/polymathiccoder<br />Twitter<br />http://twitter.com/polymathiccoder<br />
  • 4. Warning<br />This presentation is very long and covers a lot of material<br />
  • 5. Outline<br />Part I: Unit Testing<br />Vanilla Is Boring<br />Stay PUT<br />All Those Theories<br />All that CRUD<br />Fakery<br />Mockery<br />Part II: TDD<br />Part III: BDD<br />Part IV: Tools<br />Cross-cutting<br />Story Time<br />When I tell you a story<br />Libra-palooza<br />When I tell you about the coolness of some libraries<br />
  • 6. There Will Be Code!<br />
  • 7. Part I - Unit Testing<br />
  • 8. What is Unit Testing?<br />What is a Unit?<br />The smallest piece of code that<br />Can be isolated<br />Is testable<br />Targets specific functionality<br />Hmm… Let’s see…<br />A statement?<br />A branch?<br />A method?<br />A basis path within a method<br />Defined by the flow of execution from the start of a method to its exit<br />How many a method has depends on cyclomatic complexity<br />N decisions = 2^N basis possible paths<br />Infinite paths<br />
  • 9. What is Unit Testing?<br />What kind of Testing?<br />The verification of<br />Intent<br />The answer to the question:<br />Does the code do what the developer intended for it to do?<br />Reliability<br />The answer to the question:<br />Can I depend or build upon it?<br />
  • 10. xUnitand xUnit Concepts<br />xUnit is any Unit Testing framework<br />Based on SUnit, a Smalltalk project, design by Kent Beck<br />Concepts<br />Test fixtures<br />Test case<br />Assertions<br />Test execution<br />Test suites<br />
  • 11. Good Test Data Include<br />Valid data<br />Invalid data / Boundary conditions<br />Different Formats<br />Different Order<br />Wide Range<br />Null data<br />Error Conditions<br />
  • 12. Good Tests<br />Cover all possible basis paths<br />Have Self-Describing Names<br />Are Cohesive<br />Independent of each other<br />Reliable<br />Repeatable<br />Fast<br />
  • 13. The Most Opinionated Slide!<br />Lame Excuses!<br />Waste of time<br />Nuisance<br />Distraction from real work<br />Hard to maintain<br />Blah… Blah… Blah…<br />3 bullet points<br />Quit being lazy!<br />Do yourself a favor and get with the program!<br />You are attitude is the fastest way to get inducted to the Hall of Lame<br />
  • 14. xUnit Frameworks in Java<br />The most popular frameworks<br />JUnit<br />A port of the original SUnit to Java by Erich Gamma and Kent Beck<br />http://junit.org/<br />TestNG<br />http://testng.org/<br />This presentation focuses on JUnit<br />
  • 15. Vanilla Is Boring<br />
  • 16. Story Time<br />
  • 17. The Quadratic Equation<br />
  • 18. The Quadratic Equation<br />
  • 19. The Code<br />QuadraticEquation.java<br />https://github.com/PolymathicCoder/LeTourDeXUnit/blob/master/src/main/java/com/polymathiccoder/talk/xunit/domain/QuadraticEquation.java<br />
  • 20. Testing the Quadratic Equation<br />Vanilla Unit Tests<br />
  • 21. The Quadratic Equation<br />3 possible data combinations that must be tested<br />Coefficients yielding a negative discriminant<br />Coefficients yielding a discriminant equals to zero<br />Coefficients yielding a positive discriminant<br />3 different outcomes<br />The equation has no real solution<br />The equation has one real solution<br />The equation has two real solution<br />3 different basis paths<br />
  • 22. JUnit Annotations<br />Test suites<br />@RunWith<br />Naming Convention<br /><Class Name>Test<br />Test fixtures<br />Set Up<br />@Before<br />@BeforeClass<br />Tear Down<br />@After<br />@AfterClass<br />
  • 23. JUnit Annotations<br />Tests<br />@Test<br />Naming Convention<br /><Method Name>_<State Under Test>_<Expected Behavior><br />Assertions<br />assertTrue, assertEquals, assertThat, assertNull, etc…<br />
  • 24. Testing Private Methods<br />Don’t test them<br />Relax the visibility to make the code testable<br />Annotate Google Guava’s @VisibleForTesting<br />Use a nested test class<br />Use reflection<br />
  • 25. The Code<br />QuadraticEquationTest.java<br />https://github.com/PolymathicCoder/LeTourDeXUnit/blob/master/src/test/java/com/polymathiccoder/talk/xunit/domain/QuadraticEquationTest.java<br />
  • 26. Stay PUT<br />
  • 27. Testing the Quadratic Equation<br />Parameterized Unit Tests<br />
  • 28. Stay PUT<br />Parameterized or Data-Driven Unit Tests<br />Annotations<br />@RunWith(Parameterized.class)<br />To provide the parameters to be supplied via constructor injection<br />@Parameters public static Collection<Object[]> parameters()<br />@Parameter Object[] parameters<br />
  • 29. Libra-palooza<br />Featuring the coolest JUnit extensions and libraries<br />
  • 30. Libra-palooza<br />Hamcrest<br />Matchers Library<br />Included in JUnit since 4.4<br />http://code.google.com/p/hamcrest/<br />Unitils<br />Reflection-based assertions, etc…<br />http://unitils.org<br />Fest<br />Fluent Assertion, etc…<br />http://code.google.com/p/fest/<br />
  • 31. The Code<br />QuadraticEquationParameterizedTest.java<br />https://github.com/PolymathicCoder/LeTourDeXUnit/blob/master/src/test/java/com/polymathiccoder/talk/xunit/domain/QuadraticEquationParameterizedTest.java<br />
  • 32. All Those Theories<br />
  • 33. Story Time<br />
  • 34. The River Crossing Puzzle<br />
  • 35. The River Crossing Puzzle<br />
  • 36. The Code<br />RiverCrossing.java<br />https://github.com/PolymathicCoder/LeTourDeXUnit/blob/master/src/main/java/com/polymathiccoder/talk/xunit/domain/RiverCrossing.java<br />
  • 37. Testing the River Crossing Puzzle<br />Theories<br />
  • 38. The River Crossing Puzzle<br />9 possible combinations that must be tested<br />Wolf and Cabbage left behind<br />Cabbage and Wolf left behind<br />Wolf and Goat left behind<br />Goat and Wolf left behind<br />Goat and Cabbage left behind<br />Cabbage and Goat left behind<br />Wolf and Wolf left behind<br />Goat and Goat left behind<br />Cabbage and Cabbage left behind<br />3 different outcomes<br />One gets transported to the other side<br />One of two left behind eats the other<br />Error trying to transport more than one<br />18 basis paths<br />
  • 39. The River Crossing Puzzle<br />9 possible combinations that must be tested and 3 basis paths yielding 3 different outcomes<br />9 x 3 = 18 Vanilla Tests<br />3 x 3 = 9 Parameterized Tests<br />1 x 3 = 3 Theories<br />
  • 40. Theories<br />Running tests with every possible combination of data points<br />Annotations<br />@RunWith(Theories.class)<br />To provide the parameters to be supplied via constructor injection<br />@DataPoints public static Object[] objects<br />@DataPoint public static object<br />
  • 41. Assumptions and Rules<br />Assumptions<br />assumeThat, assumeTrue, assumeNotNull, etc…<br />Rules<br />Allowing for alterations in how test methods are run and reported<br />@Rule<br />Injects public fields of type MethodRule<br />ErrorCollector<br />ExpectedException<br />ExternalResource<br />TemporaryFolder<br />TestName<br />TestWatchman<br />Timeout<br />Verifier<br />
  • 42. The Code<br />RiverCrossingTheory.java<br />https://github.com/PolymathicCoder/LeTourDeXUnit/blob/master/src/test/java/com/polymathiccoder/talk/xunit/domain/RiverCrossingTheory.java<br />
  • 43. All That CRUD<br />
  • 44. Story Time<br />
  • 45. The Employee Database<br />
  • 46. The Employee Database<br />Simple Domain Object<br />Employee<br />Id - Number<br />Name - Text<br />Data-Access Object Interface<br />EmployeeDao – CRUD operations<br />Two implementations<br />EmployeeCollectionImpl – A Java Collection implementation<br />EmployeeDaoDbImpl – A JDBC implementation<br />
  • 47. The Code<br />Employee.java<br />https://github.com/PolymathicCoder/LeTourDeXUnit/blob/master/src/main/java/com/polymathiccoder/talk/xunit/domain/Employee.java<br />EmployeeDao.java<br />https://github.com/PolymathicCoder/LeTourDeXUnit/blob/master/src/main/java/com/polymathiccoder/talk/xunit/repository/EmployeeDao.java<br />
  • 48. The Code<br />EmployeeDaoCollectionImpl.java<br />https://github.com/PolymathicCoder/LeTourDeXUnit/blob/master/src/main/java/com/polymathiccoder/talk/xunit/repository/EmployeeDaoCollectionImpl.java<br />EmployeeDaoDbImpl.java<br />https://github.com/PolymathicCoder/LeTourDeXUnit/blob/master/src/main/java/com/polymathiccoder/talk/xunit/repository/EmployeeDaoDbImpl.java<br />
  • 49. Testing the Employee Database<br />All that CRUD<br />
  • 50. Testing Persistence Code <br />Data<br />Define the initial state of data before each test<br />Define the expected state of data after each test<br />Ensure that the data is in a known state before you run the test<br />Run the test<br />Compare against the expected dataset to determine the success or failure of the test<br />Clean after yourself if necessary<br />
  • 51. Testing Persistence Code <br />Notes of Java Collections<br />An Immutable/un-modifiable collection is NOT necessarily a collection of Immutable/un-modifiable objects<br />Notes on testing database code<br />Use a dedicated database instance for testing per user<br />Use an in-memory database if you can<br />HSQLDB<br />H2<br />Etc…<br />
  • 52. Custom Hamcrest Matches<br />Hamcrest Matches<br />Write your own custom type-safe matcher<br />One asserting per unit test<br />Generating a readable description to be included in test failure messages<br />https://github.com/PolymathicCoder/LeTourDeXUnit/blob/master/src/test/java/com/polymathiccoder/talk/xunit/domain/matcher/EmployeeIsEqual.java<br />
  • 53. Libra-palooza<br />Featuring the coolest JUnit extensions and libraries<br />
  • 54. Libra-palooza<br />HamSandwich<br />http://code.google.com/p/hamsandwich/<br />Hamcrest Text Patterns<br />http://code.google.com/p/hamcrest-text-patterns/<br />
  • 55. The Code<br />EmployeeDaoCollectionImplTest.java<br />https://github.com/PolymathicCoder/LeTourDeXUnit/blob/master/src/test/java/com/polymathiccoder/talk/xunit/repository/EmployeeDaoCollectionImplTest.java<br />
  • 56. Libra-palooza<br />Featuring the coolest JUnit extensions and libraries<br />
  • 57. Libra-palooza<br />DBUnit<br />A JUnit extension used to unit test database code<br />Export/Import data to and from XML<br />Initialize database into a known state<br />Verify the state of the database against an expected state<br />http://www.dbunit.org/<br />
  • 58. Libra-palooza<br />Unitils<br />Database testing, etc…<br />Integrates with DBUnit<br />Annotations<br />@RunWith(UnitilsJUnit4TestClassRunner.class)<br />@DataSet<br />@ExpectedDataSet<br />@TestDataSource<br />http://unitils.org<br />
  • 59. The Code<br />EmployeeDaoDbImplTest.java<br />https://github.com/PolymathicCoder/LeTourDeXUnit/blob/master/src/test/java/com/polymathiccoder/talk/xunit/repository/EmployeeDaoDbImplTest.java<br />
  • 60. Fakery<br />
  • 61. The Mailer<br />
  • 62. The Mailer<br />Simple email sender that uses the JavaMailAPI<br />Fakes<br />Dumpster<br />A fake SMTP server to be used in unit tests<br />http://quintanasoft.com/dumbster/<br />ActiveMQ<br />An embedded broker (Make sure to disabled persistence)<br />http://activemq.apache.org/<br />
  • 63. The Code<br />Mailer.java<br />https://github.com/PolymathicCoder/LeTourDeXUnit/blob/master/src/main/java/com/polymathiccoder/talk/xunit/repository/Mailer.java<br />MailerTest.java<br />https://github.com/PolymathicCoder/LeTourDeXUnit/blob/master/src/test/java/com/polymathiccoder/talk/xunit/repository/MailerTest.java<br />
  • 64. Libra-palooza<br />Featuring the coolest JUnit extensions and libraries<br />
  • 65. Libra-palooza<br />XMLUnit<br />http://xmlunit.sourceforge.net/<br />HTMLUnit<br />http://htmlunit.sourceforge.net/<br />HttpUnit<br />http://httpunit.sourceforge.net/<br />Spring Test<br />http://static.springsource.org/spring/docs/3.1.x/spring-framework-reference/html/testing.html<br />
  • 66. Mockery<br />
  • 67. Story Time<br />
  • 68. The Egyptian Plover & the Nile Crocodile<br />
  • 69. The Egyptian Plover & the Nile Crocodile<br />Herodotus in “The Histories” claimed that there is a symbiotic relationship between the Egyptian Plover and the Nile Crocodile<br />
  • 70.
  • 71. The Code<br />EgyptianPlover.java<br />https://github.com/PolymathicCoder/LeTourDeXUnit/blob/master/src/main/java/com/polymathiccoder/talk/xunit/domain/EgyptianPlover.java<br />
  • 72. Testing the Egyptian Plover & the Nile Crocodile<br />Mocking<br />
  • 73. The Egyptian Plover & the Nile Crocodile<br />3 different basis path<br />The plover finds the crocodile’s mouth closed and flies away<br />The plover finds the crocodile’s mouth open, doesn’t find any leeches, then flies away<br />The plover finds the crocodile’s mouth open, picks the leeches, then flies away<br />3 different outcomes<br />False is returned indicating that the plover didn’t eat<br />An exception is thrown and False is returned indicating that the plover didn’t eat<br />True indicating that the plover ate<br />
  • 74. Mocking<br />Dependencies need to be mock to test in isolation<br />Simulating an object to mimic the behavior of a real object in a controlled manner<br />
  • 75. Mocking<br />Dependencies need to be mock to test in isolation<br />Simulating an object to mimic the behavior of a real object in a controlled manner<br />
  • 76. Libra-palooza<br />Featuring the coolest JUnit extensions and libraries<br />
  • 77. Libra-palooza<br />Mockito<br />The coolest mocking framework there is<br />Annotations<br />@RunWith(MockitoJUnitRunner.class)<br />@Mock<br />@InjectMocks<br />@Spy<br />Stub method calls<br />Verify interactions<br />http://code.google.com/p/mockito/<br />
  • 78. The Code<br />EgyptianPloverTest.java<br />https://github.com/PolymathicCoder/LeTourDeXUnit/blob/master/src/test/java/com/polymathiccoder/talk/xunit/domain/EgyptianPloverTest.java<br />
  • 79. This is a digitally constructed image from the Warren Photographic Image Library of Nature and Pets<br />http://www.warrenphotographic.co.uk/<br />
  • 80. Miscellaneous Topics<br />
  • 81. Test Suites and Groups<br />Annotations<br />@Category<br />@RunWith(Categories.class)<br />@IncludeCategory<br />@ExcludeCategory<br />@SuiteClasses<br />
  • 82. The Code<br />FastTest.java<br />SlowTest.java<br />SlowAndFastTest.java<br />SlowTestSuite.java<br />https://github.com/PolymathicCoder/LeTourDeXUnit/tree/master/src/test/java/com/polymathiccoder/talk/xunit/misc<br />
  • 83. Miscellaneous Topics<br />Testing AOP (Aspect-Oriented Programming) Code<br />Unit Testing the Advice<br />Verify the execution of the Advice when Joint Point is reached<br />Testing Concurrency<br />In Junit<br />Concurrent test execution<br />Annotations<br />@Concurrent<br />@RunWith(ConcurrentSuite.class)<br />GroboUtils<br />http://groboutils.sourceforge.net/<br />ConcJUnit<br />http://www.cs.rice.edu/~mgricken/research/concutest/concjunit/<br />
  • 84. Part II – TDDTest-Driver Development<br />
  • 85. This Is How We Have Been Doing It<br />
  • 86. Why Not Test First Instead?<br />Most of us are just not disciplined to test last<br />In order to determine a sets of verifications in the form tests to fulfill the requirements<br />No room for misunderstanding<br />Testable code is good code<br />When you let testing drive the implementation<br />A test-driven implementation is testable by definition<br />No refactoring will be necessary<br />
  • 87. What if?<br />Design<br />Testing<br />Write failing tests<br />Implementation<br />Get the tests to pass<br />
  • 88. The TDD Way<br />
  • 89. Part III – BDDBehavior-Driver Development<br />
  • 90. TDD Is Great, But…<br />TDD works<br />But Thinking of requirements in terms of tests is NOT easy<br />Syllogism<br />Tests verify requirements<br />Requirements define behavior<br />Tests verify behavior<br />Neuro-Linguistic Programming (NLP) suggests that the words we use influence the way we think<br />What if we start using terminology that focuses on the behavioral aspects of the system rather than testing?<br />
  • 91. The BDD Way<br />BDD is simply a rephrased TDD<br />The focus on behavior<br />Bridges the gap between Business users and Technologists<br />Makes the development goals and priorities more aligned with business requirements<br />TDD vs. BDD<br />Verification<br />State-Based<br />Bottom-Up approach<br />Specification<br />Interaction-Based<br />Outside-In Approach<br />
  • 92. The BDD Way<br />“Behavior-Driven Development (BDD) is about implementing an application by describing it from the point of view of its stakeholders” – Jbehave.org<br />
  • 93. BDD Concepts<br />Story<br />A description of a feature that has business value<br />As a [Role],<br />I want to [Feature]<br />So that I [Value]<br />Sounds familiar?<br />Agile for you!<br />
  • 94. BDD Concepts<br />Scenario<br />A description of how the user expects the system to behave using a sequence of steps<br />Step<br />Can be a context, an event, or an outcome<br />Given [Context]<br />When [Event]<br />Then [Outcome]<br />
  • 95. BDD in Java<br />JBehave<br />Very powerful and flexible<br />Well-documented<br />Separation of story files from code<br />http://jbehave.org<br />EasyB<br />http://www.easyb.org/<br />Concordion<br />http://www.concordion.org/<br />
  • 96. Testing the Quadratic Equation<br />Stories, Scenarios, and Steps<br />
  • 97. The Quadratic Equation<br />3 possible data combinations that must be tested<br />Coefficients yielding a negative discriminant<br />Coefficients yielding a discriminant equals to zero<br />Coefficients yielding a positive discriminant<br />3 different outcomes<br />The equation has no real solution<br />The equation has one real solution<br />The equation has two real solution<br />3 different basis paths<br />
  • 98. BDD with JBehave<br />Write the story<br />Map the steps to a POJO<br />@Given<br />@When<br />@Then<br />Configure the stories<br />@Configure<br />Run the stories<br />@RunWith(AnnotatedEmbedderRunner.class)<br />@UsingEmbedder<br />@UsingSteps<br />View Reports<br />
  • 99. The Code<br />quadraticEquation.story<br />https://github.com/PolymathicCoder/LeTourDeXUnit/blob/master/src/test/java/com/polymathiccoder/talk/xunit/domain/quadraticEquation.story<br />QuadraticEquationStories.java<br />https://github.com/PolymathicCoder/LeTourDeXUnit/blob/master/src/test/java/com/polymathiccoder/talk/xunit/domain/QuadraticEquationStories.java<br />
  • 100. Part IV - Tools<br />
  • 101. Tools<br />Code Coverage<br />Cobertura<br />http://cobertura.sourceforge.net/<br />EMMA<br />http://emma.sourceforge.net/<br />Continuous Integration<br />On the server: Jenkins/Hudson<br />http://jenkins-ci.org/<br />On your IDE: Infinitest<br />http://infinitest.github.com/<br />
  • 102. Q & A<br />
  • 103. Material<br />The Slides<br />http://www.slideshare.net/PolymathicCoder<br />The Code<br />https://github.com/PolymathicCoder/LeTourDeXUnit<br />The Speaker<br />Email: abdelmonaim.remani@gmail.com<br />Twitter: @polymathiccoder<br />LinkedIn: http://www.linkedin.com/in/polymathiccoder<br />
  • 104. Thank You<br />

×