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

Outside In: an introduction to TDD

Matt Ball
September 09, 2013

Outside In: an introduction to TDD

Some slides I used for some TDD training in 2013

Matt Ball

September 09, 2013
Tweet

More Decks by Matt Ball

Other Decks in Programming

Transcript

  1.  TDD Methodology, part 1:  What is TDD, and

    why is it important?  Requirements, tests and code.  Outside-in.  Changing gears.  Hands on TDD, part 1  Rock Paper Scissors Lizard Spock.  Lunch  TDD Methodology, part 2:  End-to-end.  Dependency injection and mocking.  Bug hunting.  Pair programming.  When tests go bad!  Hands on TDD, part 2  Designing an email sign-up service using TDD. Today
  2.  Before we describe the process, we must understand what

    it is.  Before we explain what it is, it is easier to understand why it is important. What is TDD?
  3.  How do you know your code works, if you

    do not test it?  How can you test your code, if you do not understand the problem?  How can you reason about a problem, if you try to solve it perfectly, first time? Why is TDD important?
  4.  How do you know your code works, if you

    do not test it? Why is TDD important?
  5.  How can you test your code, if you do

    not understand the problem? Why is TDD important?
  6.  How can you reason about a problem, if you

    try to solve it perfectly, first time? Why is TDD important?
  7.  A method by which we can ensure that our

    code works, and stays working.  A method which forces us to understand the requirement, problem and solution – before we commit to implementation decisions.  A method which forces us to concentrate only on small parts of the problem, at any given time. So again, what is TDD?
  8.  A method by which we can ensure that our

    code works, and stays working. So again, what is TDD?
  9.  A method which forces us to understand the requirement,

    problem and solution – before we commit to implementation decisions. So again, what is TDD?
  10.  A method which forces us to concentrate only on

    small parts of the problem, at any given time. So again, what is TDD?
  11.  Red  Write a test, watch it fail. 

    Green  Make the test pass, as quickly as possible.  Refactor  Engineer cleaner-code. Process
  12. Red  Write a test script to express a required

    behaviour.  Run the test, watch and understand the failure.  Be very wary of tests that pass first time! In a compiled language, it is likely that the first failure is a compiler error.
  13. Green  Write production code to make the test pass.

     Make the test pass as quickly as possible – committing any sins required; speed trumps quality.  Don’t engineer your code – we only need to solve the problem.
  14. Refactor  Now the problem is solved – you can

    now engineer a clean solution.  Run the tests regularly – to ensure that you don’t break anything.  Be sure to refactor test code as well – the quality, maintainability and readability of test code is just as important as production code.
  15.  Code is expensive:  Expensive to write  Expensive

    to maintain  The more code that exists, the more bugs can exist. Bugs cost money.  So lets not write too much code, eh?  i.e. if there isn’t a requirement, you can’t write a test, and without a test, you can’t write production code.  Keep honest. Requirements, Tests, Code
  16.  Before you can write a test, you need to

    know the requirements:  Who or what is the consumer?  What are the expected behaviours?  How do we know if it worked, or if something went wrong?  Can you get some concrete examples? Scenarios? User stories? Requirements
  17.  A unit test is an isolated script designed to

    test software and assert expected behaviours are expressed.  A good unit test has three main parts:  Arrange (Given)  Act (When)  Assert (Then)  A great unit test should be readable – it should obviously express the requirement.  If you get “stuck” not knowing how to write a test – you might not understand the requirement.  Remember (this is important) – you are testing behaviours; not new code. Tests
  18.  Your test code will describe the API of your

    production code:  If you have access modifiers at your disposal – then tests call public members; everything else is implementation (internal or private).  Don’t get too DRY:  DRY is the enemy of Decoupling  Keep your production code lean – write what is required to make the test pass, nothing more. Code
  19.  As a software developer, it is almost natural to

    come inside-out:  Database  DAL  Domain Model  UX/UI, Web Service etc.  But how often does that lead to feature drift, and functionality not actually required?  Or how often do you get to that outer-layer, just to realised you don’t quite have what you need?  This is an anti-pattern – let’s stop doing that! Outside-in
  20.  Behaviours and requirements come from the outside world –

    as such software exposes an API to consumers, which is used to “reach in” and take advantage of functionality.  Since we’re testing behaviours and requirements – our unit tests must also live on the outside; and they let us drive our development inwards.  Unit tests interact with our outer API.  Each layer then defines requirements on each internal layer as it get towards the core of the application. Outside-in
  21.  Benefits:  Reduced surface area of testing, which means

    fewer tests – less code to write and maintain. Cheaper.  Build only what you need – maintain focus on a requirement in each layer and avoid feature drift.  Helps to maintain a design focus on the external API.  Stops your test suite from being highly coupled to your internal implementation – test your behaviours, and not your implementation. Outside-in
  22. Outside-in  “But hey! Aren’t those pretty big tests?” 

    Yes – they test behaviours, through the entire application stack.  But a chain is only as strong as the weakest link – if there is a bug in an internal component; it will surface on the outer layer.  If we get really stuck… we can always change down gears.
  23.  We like to cruise along, on big fast roads

    in 5th gear. No obstacles, nothing to think about.  But what happens when we hit a few junctions, and we don’t know where we’re going?  And what if we end up off-road? What then?  This is actually fairly analogous to TDD. Changing gears
  24.  In TDD we’re normally driving in 4th gear –

    having to find our way; but still able to go quite fast as we drive in development of behaviours.  The key here is that we’re still solving the problem.  5th gear is reserved for when are driving in development of a well known problem – something solved time and time again. Here we can engineer at the same time.  When we hit a difficult problem – it is like needing to go off-road. We’ve got to drop down gears and take it slowly.  Now is a good time to use smaller, implementation based tests – to feel your way through the problem.  But afterwards – you should feel safe to delete them! Changing gears
  25. The method of testing software systems as a whole, and

    the processes involved in deploying them. End-to-End Testing
  26.  Has a lot in common with Continuous Delivery. 

    Or rather – is probably essential for good continuous delivery.  Tests run against the software system, as it is deployed to a production-like environment.  Puts an emphasis on ensuring that the deployment pipeline is functioning, and that software correctly interacts with integrated systems. End-to-End Testing
  27.  Some of the benefits include:  Forces you to

    deal with some hard problems – first (like deployment, automation etc).  Confidence that all software systems involved – work together in a production-like environment. End-to-End Testing
  28.  The fastest tests to write and run - ones

    that provide the most benefit-to-cost; are not end- to-end tests.  So how do we test our own code in isolation from other software systems?  Databases  Logging  3rd Party software and services  With Dependency Injection and Mocking DI and Mocking
  29.  Dependency Injection:  Generally requires you to design by

    contract (i.e. rely heavily on interfaces and base classes).  Objects with dependencies on other objects – must require that those dependencies are provided at initialization/construction.  Can be used in conjunction with Inversion of Control to help reduce coupling and cyclic dependency graphs.  But DI is a design principle – what has that got to do with testing? DI and Mocking
  30.  By using DI and Mock objects we can: 

    Ensure that our software is correctly integrated with external components and systems  Exclude other software components and systems from our code-under-test  Simulate behaviours in other components and systems that we need our software to deal with  Use them to “peek” into the internals of our own code. DI and Mocking
  31.  We do so by injecting a mock object as

    a dependency to our software.  The mock object is a “stand-in” – an object of our own creation that behaves in a way that aids the unit test suite; to prove or disprove correct behaviour from the code-under-test.  We can hand roll mock objects – but there are also a wealth of frameworks that help use create mock objects quickly. DI and Mocking
  32.  TDD is also a great solution for hunting, trapping

    and killing bugs in software.  If you think of a bug as a requirement – you can easily replicate the issue in a unit test (trap it) and then fix the problematic production code (kill it). Bug hunting
  33. Two heads, are better than one. It’s double the pleasure,

    and triple the fun! – Power Tool, Two Heads  TDD helps maintain focus on solving problems.  Pair programming helps share learning of a problem.  The two work incredibly well in tandem. Pair programming
  34.  Refactoring code causes test cases to break.  Chances

    are that you’re not testing against a focused API – but rather internal implementation.  Interdependent test cases.  A test case that is expecting a “state” created by another test, i.e. not a unit test, testing in isolation.  Discover and fix this will tools that randomize your test execution. When tests go bad (TDD anti-patterns)
  35.  Non-descriptive test names:  Test cases should express the

    behaviour that they are asserting in their name.  Steps within a test case should be easy to read and relate to the name of the test.  Other developers may need to maintain your test suite.  Test suite code is a second class citizen:  Not well refactored.  Hard to maintain. When tests go bad (TDD anti-patterns)
  36.  Tests with waits or thread sleeps:  Probably testing

    times – use DI and mocking.  UI testing:  Brittle.  UI undergoes refactoring perhaps more often than code.  Expensive to write.  Expensive to maintain. When tests go bad (TDD anti-patterns)
  37.  Test Driven Development – Kent Beck  Growing Object-Oriented

    Software, Guided by Tests – Freeman/Pryce  The Cucumber Book – Wynne/Hellesoy Further learning