Upgrade to Pro — share decks privately, control downloads, hide ads and more …

TDD & SOLID Design Principles: Part One

TDD & SOLID Design Principles: Part One

David Julia

November 03, 2016
Tweet

More Decks by David Julia

Other Decks in Programming

Transcript

  1. 1. Write a test 2. Make it compile 3. Make

    it run 4. Remove duplication To quote Kent Beck...
  2. The first three phases need to go by quickly, so

    we get to a known state with the new functionality. You can commit any number of sins to get there, because speed trumps design, just for that brief moment. To quote Kent Beck...
  3. A three legged horse can’t gallop. The first three steps

    of the cycle won’t work without the fourth. Good design at good times. Make it run, make it right. To quote Kent Beck...
  4. The goal is defect free software that is easy to

    change at a predictable, sustainable pace. Assumption:
  5. Easy to add features confidently and quickly. Easy to Change?

    eg. “And now I want the service to return the sale price in Euros too!”
  6. TDD’d projects should feel better and better to work in!

    Sustainable Pace? “Implementing that new feature is going to be so easy because we can just...”
  7. Red => Green => Refactor. - “Don’t think, run the

    test” - “okay, now let’s make it better” - “Forget about the controller layer, we’re just making this test pass” Fast Feedback & Focus
  8. On design: Your tests are the first consumer of your

    class/package’s API Fast Feedback
  9. Single Responsibility Principle (+TDD) Open Closed (+TDD enables) Liskov Substitution

    (+language enables) Interface Segregation (+TDD reveals violations) Dependency Inversion (+TDD often forces) Good Design
  10. Mocking TODO: example 1st with hand rolled fake + Interface,

    then with Mockito mock Show separating stubbing from verification of interactions & why it's nice Do not mock value objects
  11. Process (top down) 1. “High level test for desired behavior”

    2. Locate highest layer @ which to make a change (entrypoint). 3. Write a test, optionally Mocking significant collaborators. Get them to pass. 4. Write tests for collaborators, make them pass. 5. Repeat 4 for collaborator’s collaborators. 6. Run high level test. If it passes, you’re good.
  12. How do I test this!? It’s so complicated. There’s so

    much going on!!! Extract collaborating object. 1. Move implementation over to new class. 2. Run existing tests. If Green, great! 3. Copy behavior specific tests to collaborator unit tests. Green? Great! 4. Delete tests in original object and Mock interaction (optional/per best judgment)
  13. How do I test this existing code!? Dependency Injection is

    your friend… Break hard dependencies. function ShoppingController(){ this.on("theyWannaCheckoutEvent", function(){ window.PaymentGateway.chargeCustomer(50.50); }) } describe(“A test that really charges the customer”, function(){ it(“Drains David’s Bank Account”, function(){ new ShoppingController().trigger(“theyWann aCheckoutEvent”); //Uhhhh expect me to check my balance //in the Charles Schwab app... } }
  14. How do I test this!? function ShoppingCtrl(paymentGateway){ this.on("theyWannaCheckoutEvent", function(){ paymentGateway.chargeCustomer(50.50);

    }) } describe(“A test that really charges the customer”, function(){ it(“Drains David’s Bank Account”, function(){ mockPaymentGateway = { chargeCustomer: jasmine.createSpy() }; new ShoppingCtrlr(mockPaymentGateway). trigger(“theyWannaCheckoutEvent”); expect(mockPaymentGateway.chargeCustomer). toHaveBeenCalledWith(50.50); } }
  15. Testing within frameworks -Similarly, do not test the framework. -Test

    according to Responsibilities (SRP) “What behavior is this object responsible for”
  16. Testing within Frameworks @RestController class UserController{ @Autowired public UserController(UserService usvc){...}

    @RequestMapping(value ="/users/{userId}", method = RequestMethod.GET) public Account getAccount(@PathVariable Long userId){return null;} } @Test public void getAccount() throws Exception { when(userService.findUser(anyInt())).thenReturn("element"); this.mockMvc.perform(get("/users/123") andExpect(status().isOk()); } @Test public void getAccount() throws Exception { when(userService.findUser(anyInt())).thenReturn("element"); this.mockMvc.perform(get("/users/123") andExpect(status().isOk()); verify(userService.findUser(123); } @RestController class UserController{ @Autowired public UserController(UserService usvc){...} @RequestMapping(value ="/users/{userId}", method = RequestMethod.GET) public Account getAccount(@PathVariable Long userId){ userService.findUser(123) return null; }} Test that we correctly interact with the framework
  17. It’s possible to have 100% Coverage. 0% Confidence. Just exercise

    every line of code without any assertions. Yes, I’ve really seen this when there was a bad incentive structure.