Unit Testing Case Study for COJUG - 05.11.2010

  • View

  • Download

Embed Size (px)


Nick Watts' "Unit Testing Case Study: How Ohio Mutual Insurance Group Got Started" presentation. Presented to the Columbus Java Users Group on May 11th, 2010 in Dublin, Ohio.


  • 1.Nick WattsSenior Programmer/Analyst for OMIG Technical Editor of NFJS, The Magazine

2. Extensionof the No Fluff Just Stuff (NFJS) travelling symposium. Only NFJS speakers write articles for the magazine. Delivered as PDF and ePub file downloads. www.nofluffjuststuff.com/home/magazine_subs cribe @nfjsmag on Twitter. Copyright, Ohio Mutual Insurance Group 2 3. Testing Education Getting Started in Unit Testing Mock Objects Copyright, Ohio Mutual Insurance Group 3 4. Over2 3 years we read articles about, attended user group meetings about, took a class as part of a Masters degree about and attended conference sessions on testing. Bewilderment is an appropriate response for the first 6 12 months (or so). Copyright, Ohio Mutual Insurance Group 4 5. Thearticle that finally made unit testingmake sense to me: Evolutionary architecture and emergentdesign: Test-driven design, Part 1 byNeal Ford. http://www.ibm.com/developerworks/java/library/j-eaed2/ Copyright, Ohio Mutual Insurance Group5 6. Overthe 2 3 year learning period we also practiced writing unit tests. We started with the simplest possible JUnit tests we could find to do. Neal Ford asks the question, "What is the simplestthing for which I can write a test?" Over time we were able to improve and write tests for increasingly trickier situations. Trying overly-complex tests was a common first misstep. Copyright, Ohio Mutual Insurance Group6 7. Itwas absolutely necessary to understand the code in order to begin testing it. The newest member of the team has been on for over 2 years. Copyright, Ohio Mutual Insurance Group7 8. This was a v e r y l o n g process! Do not expect to get immediately satisfying results. Though some people present it so, building a significant set of unit tests on an existing code base is not easy or quick. Copyright, Ohio Mutual Insurance Group 8 9. This all seems so obvious, why am Itelling you? It helps to hear that someone else went through the same experience. Speakers and authors glaze over real details, such as this is REALLY hard to do in REAL life, to remain brief. Copyright, Ohio Mutual Insurance Group 9 10. Realcode from a live production system that has been in place for over five years. Brief analysis of some of the core business classes in the system. Brief description of major hurdles to writing tests against the code. Analysis of some methods for testing the code before refactoring it. Copyright, Ohio Mutual Insurance Group 10 11. Unit testing examples are often trite and too far off from reality. I believe you either are, were, or will be as confused as my team was when we began writing unit tests. I hope youll learn more by seeing real code than by seeing contrived examples. Copyright, Ohio Mutual Insurance Group 11 12. Copyright, Ohio Mutual Insurance Group12 13. Copyright, Ohio Mutual Insurance Group13 14. Corebusiness domain package of alegacy JEE system (e-Quipd) atop alegacy COBOL policy managementsystem (POINT IN). Roughly mirrors the logical view of a policy in POINT IN. e-Quipd was not built with unit testing in mind. Copyright, Ohio Mutual Insurance Group 14 15. Imnot sure where to start because Icant understand the code. Complexity Idont want to write a test that usesstatic data because its not a good test. Validity of Simple Tests Copyright, Ohio Mutual Insurance Group15 16. Its too much of a hassle to write a test because of all the dependencies. Unruly Coupling Imnot supposed to write unit tests for methods that connect to a database. Unexpected Database Connections Copyright, Ohio Mutual Insurance Group16 17. This is the ugly truth in real code. Complexity abounds (both accidental and intentional). The complexity made the task of testing seem hopeless at first. We were unable to decide where to start because there is no clear entry point. Copyright, Ohio Mutual Insurance Group 17 18. Used Old fashioned elbow grease. Understood the business domain and thesystems that support it. Studied the code. Communicated with others who work on thesystem. A visualization is very helpful. yDoc is an inexpensive way to start visualizingyour code in class-sized chunks. Copyright, Ohio Mutual Insurance Group 18 19. Copyright, Ohio Mutual Insurance Group19 20. Copyright, Ohio Mutual Insurance Group20 21. Started simple: Chose the simplest classes we could find. Usually POJOs and Beans with little or no business logic. Copyright, Ohio Mutual Insurance Group21 22. public class OMIGSSNumberTest { public void test_default_constructor() {OMIGSSNumber ssn = new OMIGSSNumber();assertNotNull(ssn);assertNotNull(ssn.getSsNo());assertEquals(ssn.getSsNo(), ""); } } Copyright, Ohio Mutual Insurance Group22 23. Atfirst, we also stumbled just writing some of the simplest tests. Putting static data into tests felt unnatural. Tests with static data seemed invalid. Copyright, Ohio Mutual Insurance Group 23 24. public void test_GetModifiedJulianDate() { OMIGDate od = new OMIGDate(2008, 8, 16); assertEquals(od.getModifiedJulianDate(), 54694); } Copyright, Ohio Mutual Insurance Group24 25. More practice and learning. It just took time to realize that it was acceptable to build static test cases that didnt test all scenarios. Copyright, Ohio Mutual Insurance Group 25 26. public void test_trans_history_full_constructor() { TransactionHistoryRow row = newTransactionHistoryRow("01,"21,"05,"CPQ,"1234567,"00,"0888000,"0888z,"00,3); assertEquals("01", row.getLco()); assertEquals("21", row.getMco()); assertEquals("05", row.getPco()); assertEquals("CPQ", row.getSymbol()); assertEquals("1234567", row.getNumber()); assertEquals("00", row.getMod()); assertEquals("0888000", row.getAgencyId()); assertEquals("0888z", row.getUserId()); assertEquals("00", row.getProducerCode()); assertEquals(3, row.getActivityCode()); } Copyright, Ohio Mutual Insurance Group26 27. Instantiating an object is unnecessarilycomplex because of coupling. E.g. the BasicContract class constructor requires a PolicyKey reference be passed in, which requires that a ProductKey reference be passed in, which requires five Strings for its constructor. BasicContract is itself used in hundreds of classes. Copyright, Ohio Mutual Insurance Group27 28. Testing a method that connects to a database can be troublesome, but can be circumvented. A class that connects to a database in a constructor is more than troublesome to test (i.e. you cant just ignore a constructor). E.g. PolicyBase Copyright, Ohio Mutual Insurance Group28 29. You could repeatedly instantiate all of the objects necessary to create the one youre really interested in testing. This code isnt un-testable, its just a nuisance. Copyright, Ohio Mutual Insurance Group 29 30. public void test_basic_contract() { ProductKey prodKey = new ProductKey("01", "20", "05", "OH", "CPP"); PolicyKey polKey = new PolicyKey(prodKey); BasicContract bc = new BasicContract(polKey); assertNotNull(bc); } What about testing classes that require aBasicContract reference to instantiate? Copyright, Ohio Mutual Insurance Group30 31. Our solution was to just mock thetroublesome classes and ignore the setupof the object. You eventually want to refactor and remove the unruly coupling. So start by pretending its not there by mocking. Copyright, Ohio Mutual Insurance Group 32 32. public void test_basic_contract_best() { BasicContract bc = mock(BasicContract.class); assertNotNull(bc); } This is best because it is succinct, doesnt require custom test code and the intent is very clear. Copyright, Ohio Mutual Insurance Group33 33. Wetook the easiest path by just mocking the class we wanted to test. Most of the objects state is not needed, so we dont set it up. Notice the deep knowledge of the classes involved in this statement. Caution! Carefully consider an objects internal state. Copyright, Ohio Mutual Insurance Group34 34. mocks do not implement any logic: Theyare empty shells that provide methods tolet the tests control the behavior of all thebusiness methods of the faked class. JUnit in Action, 2nd ed. Read about mock objects in JUnit inAction by Vincent Massol and Ted Hustedand JUnit in Action, 2nd ed. by Tachiev et.al. Copyright, Ohio Mutual Insurance Group35 35. Mockitois a mock object framework. Gives you an object that looks like the real object, but may or may not, at you discretion, act like the real object. Has a very English-like syntax. Very good at working with legacy code. www.mockito.org Copyright, Ohio Mutual Insurance Group36 36. We want to test this method in PolicyBase: public void putOnHold() throws Exception { getBasicContract().setEndType("HLD"); storeAppInfo(); } Copyright, Ohio Mutual Insurance Group37 37. public void test_put_on_hold__sans_mocking() throws Exception { ProductKey prodKey = new ProductKey("01", "20", "06", "RI", "CPP"); Policy policy = new Policy(prodKey); assertTrue(StringUtils.isBlank( policy.getBasicContract().getEndType())); policy.putOnHold(); assertTrue(policy.isOnHold()); } Copyright, Ohio Mutual Insurance Group38 38. Copyright, Ohio Mutual Insurance Group39 39. Without mocking, the testwont run because itconnects to a database. Copyright, Ohio Mutual Insurance Group40 40. The previous method is hard to test because of unruly coupling and the storeAppInfo() method, which connects to a database. The solution public void test_put_on_hold() throws Exception { Policy policy = mock(Policy.class); when(policy.getBasicContract()). thenCallRealMethod(); when(policy.isOnHold()).thenCallRealMethod(); doCallRealMethod().when(policy).putOnHold(); } Copyright, Ohio Mutual Insurance Group 41 41. Tookour time with unit testing as it was a big commitment. Started with small, easy unit tests. Increased our abilities with learning and practice over time. Used mock objects to solve our biggest problems. Copyright, Ohio Mutual Insurance Group 43 42. nwatts@omig.com work email @nfjsma