Scrum Gathering 2012 Shanghai_工程实践与技术卓越分会场:how to write unit test for new code based on legacy code(姚若舟)

1,694 views
1,525 views

Published on

讲师:姚若舟
拥有十多年的软件开发经验和多年的项目管理经验,目前任职于欧特克(Autodesk)中国研发中心担任项目经理,提供内部团队敏捷实践的指导和培训。 作为组织者参与了2011年敏捷之旅上海的组织工作。同时在敏捷社区的活动中,做过多次有关测试驱动开发的分享,并通过组织Coding Dojo来推广测试驱动开发和结对编程。 个人喜爱Coding Kata,关注如何将测试驱动开发和相关工程实践更好的结合到公司的实际项目开发中,以及如何成为一个杰出的Scrum Master和敏捷教练来帮助团队和组织成长

话题介绍:
希望通过这个演讲,可以让听众明白如何在遗留代码基础上通过简单有效的设计和隔离来给新代码添加单元测试,从而让团队停止产生相应的技术负债。而且,如何拥有具备可测性的设计和做到有效的隔离,并不像有些人想的那么复杂和困难。

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

  • Be the first to like this

No Downloads
Views
Total views
1,694
On SlideShare
0
From Embeds
0
Number of Embeds
124
Actions
Shares
0
Downloads
59
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

Scrum Gathering 2012 Shanghai_工程实践与技术卓越分会场:how to write unit test for new code based on legacy code(姚若舟)

  1. 1. How to Write Unit Test forNew Code on top of Legacy CodeJoseph Yao
  2. 2. Please Read This Great Book!
  3. 3. Unit Test 101
  4. 4. What’s Unit Test?Unit tests is the idea that they are tests inisolation of individual components of software - Michael C. Feathers
  5. 5. What’s Legacy Code?Legacy code is simply code without tests.Without tests is bad code. It doesnt matter how well writtenit is; it doesnt matter how pretty or object-oriented or well-encapsulated it is. With tests, we can change the behaviorof our code quickly and verifiably. Without them, we reallydont know if our code is getting better or worse. - Michael C. Feathers
  6. 6. Make New Code Testable
  7. 7. How to Initialize Dependency Need Legacy Code New Code
  8. 8. Your Code Looks Like This?public class Car { private Engine engine; public Car() { engine = new Engine(); } public void run() { engine.start(); } public String status() { return engine.speed() > 0 ? “Move”: “Stop”; }}
  9. 9. Your Test Looks Like This?public class TestCar { @Test public void move() { Car car = new Car(); car.run(); assertEquals(“Move”, car.status()); }} Are you really do the unit test for Car?
  10. 10. Code for Car with Isolationpublic class Car { private IEngine engine; public Car(IEngine theEngine) { engine = theEngine; } public void run() { engine.start(); } public String status() { return engine.speed() > 0 ? “Move”: “Stop”; }}
  11. 11. Test for Car with Isolationpublic class TestCar { @Test public void move() { IEngine engineMock = new EngineMock(10); Car car = new Car(engineMock); car.run(); assertEquals(“Move”, car.status()); }}In fact, you don’t care how engine speed is calculated
  12. 12. I Need to Test a Private Method
  13. 13. Your Code Looks Like This?public class Order { public void addItem(Item item) { if (isValidItem(item)) { items.add(item); } } private boolean isValidItem(Item item) { more than 500 lines code… }} How can I test the private method?
  14. 14. Does Your Class Have too many Responsibilities?
  15. 15. Maybe This Code is Better?public class Order { private ItemValidator validator; public Order (ItemValidator theValidator) { validator = theValidator; } public void addItem (Item item) { if (validator. isValidItem(item)) { … } }}
  16. 16. Single Responsibility Principle
  17. 17. “Don’t Do it” unless you have a Very Strong Reason +
  18. 18. Good design is testable, anddesign that isnt testable is bad - Michael C. Feathers
  19. 19. Isolate Legacy Code
  20. 20. Too hard to get Legacy Code under Test
  21. 21. Some New Code needed to be added public class Customer { … public void purchase() { more than 500 lines of legacy code… } … } We need to log this customer purchase action after it done.Can you add unit test for new code with no impact to legacy code?
  22. 22. Add new code by Sprout Methodpublic class Customer { public void purchase() { purchaseWithoutLog(); logPurchaseAction(); } protected void purchaseWithoutLog() { more than 500 lines of legacy code… } private void logPurchaseAction() { Your new code here… }}
  23. 23. Your Test May Look Like Thispublic class TestCustomerPurchaseLog extends Customer { @Test public void log() { Customer customer = new TestCustomerPurchaseLog(); customer.purchase(); … code to verify the log action … } protected void purchaseWithoutLog() {}}
  24. 24. Some other Alternative Ways• Sprout Method – the Sample Code• Wrap Method• If the legacy class is hard to be put into test harness o Sprout Class o Wrap Class o This is quite useful when you can’t easily isolate the dependency for legacy class
  25. 25. I have a Monster Dependency to Isolate
  26. 26. How to Isolate “HttpServletRequest”?public class ARMDispatcher { public void populate (HttpServletRequest request) { String [] values = request.getParameterValues(pageStateName); if (values != null && values.length > 0) { marketBindings.put( pageStateName + getDateStamp(), values[0]); } }}
  27. 27. You only Need to get the Parameter Valuepublic class ARMDispatcher { public void populate (ParameterSource source) { String value = source.getParameterForName(pageStateName); if (value != null) { marketBindings.put( ageStateName + getDateStamp(), value); } }}
  28. 28. I need D, but it’s from A.getB().getC().getD()
  29. 29. Your Code may Look like thispublic class Customer { private Orders orders; public List<String> getAllItemNamesOfLatestOrder(){ List<String> allNames = new ArrayList<String>(); Item[] items = orders.getLatestOrder().getAllItems(); for (Item item : items) { allNames.add(item.getName()); } return allNames; }}
  30. 30. You need to Isolate the way to Get Itemspublic class Customer { private Orders orders; public List<String> getAllItemNamesOfLatestOrder(){ List<String> allNames = new ArrayList<String>(); Item[] items = getAllItemsOfLatestOrder(); for (Item item : items) { allNames.add(item.getName()); } return allNames; } protected Item[] getAllItemsOfLatestOrder() { return orders.getLatestOrder().getAllItems(); }}
  31. 31. Suggestions
  32. 32. Test Behavior instead ofTest Implementation
  33. 33. Dependency Isolation Tool is Evil
  34. 34. Q&A 新浪微博 @姚若舟 TDD Code Kata@tudou.com/home/yaoruozhou

×