it is a statement of fact for me that test driven development improves the quality of my code i believe that this is true for you too and and for all software developers unlike most techniques and most processes test driven development amplifies the talents of the software developers that adopt it so what do i mean by all of that and how can i demonstrate it let's take a look [Music] hi i'm dave farley of continuous delivery welcome to my channel if you haven't been here before please do hit subscribe and if you enjoy the content today
hit like as well i'd like to begin by thanking our sponsors equal experts octopus and spec flow they all have been supporting this channel for a long time so please do support them in turn by checking out their links in the description below test driven development allows you to learn from your code before it's too late knowing how to learn from your code enhances your ability to deliver world-class quality code at speed this is the kind of modern software engineering skill that organizations dream of having in their development teams opening up more lucrative options for
developers if you're interested in learning more about test driven development my brand new training course test driven development and behavior-driven development designed through testing is available now check the links out in the description below test driven development is not the same thing as unit testing tdd is explicitly about driving the design of our code from tests practitioners me included say that it's much more about design than it is about testing in fact in the early days i remember people debating if it should be described as test driven design rather than test driven development what this
means in practice is that we start with a test and use the test to design the external view of our code we do this in a series of simple steps red write a test run it and see it fail green write some simple code to make the test pass run it and then see the test pass and refactor make the code and test cleaner more expressive more generic and better designed at each stage we should be thinking about different things writing the test is probably the point at which we should be thinking hardest this is
us thinking about the design of our code from the outside we're consciously putting ourselves in the position of somebody using our code rather than somebody producing it because we want to use it to write this test since we don't actually have the code yet what we're really doing is designing how we expect people to use our code we'd have to be idiots to make our own lives more difficult at this point by making it hard to write our test so instead we'll try our best to design our code to be easy to test and so
easy to use if at this point your test is difficult to write then think again about how you could change the design of your code to make it easier that's the real game here this is the vital step that people that write tests after the code is done often miss this is where nearly all of the value of test driven development comes from having the tests is nice sure but that's not the real goal of test driven development the real goal is well-designed code the first step in writing your test is to think about what
it is that you want from your code this seems to be a stumbling block for lots of people some people seem to be unable to think of their system without thinking of the solution the code itself but think about that for just a second how can you come up with a solution if you don't understand the problem you can't it's not enough it's not much help for me to answer 42 if we don't say what the question was so either you're fooling yourself into thinking that you don't understand the problem or you really don't understand
the problem in which case you're not ready to write any code yet with or without tests to be fair it's sometimes difficult to figure out what test you want to write next but what you are facing here is not a problem with testing it's a problem of understanding what the next step should be in solving the problem part of the trick to test driven development is to work incrementally in very small steps i think this is the real challenge for people not used to test driven development it's not the testing it's this incremental approach to
design there's a degree to which test driven development strongly encourages this to make progress in smaller steps and this is a very good thing but it can be a bit of a culture shock if you aren't used to working that way the trick i think is to go with the flow be guided by the test driven development approach good tests in tdd express expected outcomes never specific solutions if you can't imagine throwing away your code and replacing it with an alternative implementation then your tests aren't very good we want tests that say what the code
does without saying how it does it here are a few tests can you see what the code does can you see how i implemented it i think that the answer to the first question is yes and to the second no just to demonstrate my points here are two different solutions that both make all of these tests pass this is important it means that these tests provide protection the kind of prediction that tdd promised us in the first place but many people who misread tdd as simply meaning unit testing failed to see if you write tests
after you wrote the code you tend to only test that the code that you wrote is the code that you wrote rather than testing that your goals that you set for your code were met plus there's no pressure from tests that come later to make your code testable and the testability of your code is a fantastic tool to use to steer the design let's take a look at that in a bit more detail here is some code this code takes a word as input it then reads in a list of words and looks for anagrams
of the input in the list finally it prints out how many anagrams it found and lists them all i wouldn't usually write code that looks like this it breaks too many of my own coding guidelines but i see code that looks like this all the time so i don't think it's some grotesque unrealistic caricature i think that this is representative of the kind of code that we all see in my modern software engineering book i list five qualities of code that allow us to manage complexity i recommend that you use these five qualities as hallmarks
of good design and consciously steer the design of your code to enhance them well-designed code is easy to change because it's modular cohesive has a good separation of concerns it uses abstraction to hide information and is coupled appropriately with a preference towards looser coupling this code is clearly not modular everything is together in one place you could argue that it's cohesive because everything is close together but i think that's a rather naive view of cohesion in cohesive code everything that we see is related in some way this code has a jumble of ideas all in
a single function which brings us on to separation of concerns which clears clearly terrible here this code is doing at least three different things it's reading words from a dictionary it's looking for anagrams and then it's printing out the anagrams that it finds so it definitely scores zero for separation of concerns i could have actually made it worse i could have inlined the is anagram function but i couldn't bring myself to that level of depravity to be honest abstraction well there isn't any here really essentially the show anagrams function is saying go it's about abstract
in the same way that void star or object is abstract it's too abstract to be really very helpful in us understanding what's going on finally is this code loosely coupled well obviously not we have the location of a file hard coded into the solution and we have the code printing directly onto the console all of this tied irrevocably to the finding of anagrams i said earlier that the testability of our code was a good guide to high quality design so let's take a look at the tests for this code well there isn't one really i
wrote this which allowed me to run the code and look at the results but how do i tell if it worked or not actually i'm pretty sure that there is a bug in this can you spot it if i wanted to test this how could i well i'd need to intercept attempts to write to the display somehow so that i can see what's being displayed because that's the only output from this code that's possible but it's not very easy or very pleasant i could change the code to inject the print function but now i'm just
proving my own points that i am improving the design to make my code more testable if i was being thorough i will need to replace the dictionary of words that i used so that i can control the variables specifying targeted lists of words for my tests i could do this for the code but it would mean moving files around in this example and these files are the files that my operating system uses as its dictionary for spell checking so that sounds fairly unpleasant too i hope that you will all agree that as it stands this
code is essentially untestable so let's now start from the other direction let's start with a test and the best starting point is always the simplest case that you can think of so the heart of this problem is finding anagrams let's begin by focusing only on that part of the problem we'll worry about the other stuff later on that doesn't mean that we're going to forget about it but i'm going to begin with a design assumption led by my rules for managing complexity i want my design to exhibit a clean separation of concerns and given this
problem my best guess right now is that there are three concerns we need to provide a list of words from somewhere we need to given a new word we need to find anagrams for it in that list and then we need to display the results in some way so we will assume these are three distinct parts of our code and i will start with the anagram solving part because that's the heart of the problem the simplest example that i can think of for an anagram is that a word is an anagram of itself so here's
my first test and look i've already made some design decisions for my solution here i've decided that i want a class into which i can inject a list of words immediately even before i've written any real code this design choice means that my code is now more modular and more loosely coupled it also means that the separation of concerns is improved because this code that i'm testing is only focused on one thing finding an anagram my next step is to do just enough work to make this code compile and run without any mistakes but not
yet so the test passes i want to see the tests fail that allows me to check that the test works now i can add enough code to make this single test pass i'd probably start with something like this and then after refactoring maybe end up with something like this it's in the refactoring step not the green step where we do proper implementation design because that's when we've got a passing test which reassures us that the changes that we are making are working correctly what's the next simple behavior that our code will need to show if
it's working properly maybe it should reject words that have additional letters in them because they're not true anagrams we should certainly be able to find multiple anagrams if they're in the supplied word list look at how our design true choice to separate the word list is starting to pay off now it makes it really easy to focus our test on the things that matter to us in that single test case we will iterate adding tests to make us add behavior and finally let's add a test to check out our ability to display the results here's
the test this test has taught us something new i suggested at the beginning that we'd assume three separate concerns display being one but the test that i've written so far didn't include a display because they didn't need it in this test we do so now i can decide how i'd like my design to work i can progress the design of my code at this point i could choose to inject the display on every call to list anagrams or i could do what i did here and add it at the point at which i create my
anagram solver here's the code that i wrote to make all of these tests pass if we're treating our tests as a design tool we don't need to agonize over being exhaustive with our tests looking at this there's an obvious test that i could have added we should maybe think about rejecting words that aren't anagrams i didn't think of that the first time around but now i have i could still add it and the test would pass but i don't have to because i used a different test to test the same behavior you don't have to
be perfect in your choice of tests you choose your tests to drive the design step by step let's look where we ended up we have our three concerns maybe you're worried that i haven't finished so here's the code that gives me the functional equivalence for the first horrible piece of code that i showed you clearly my code is more modular i have four pieces four modules here each dealing with a separate part of the problem i could further abstract these pieces instead of assembling the pieces here i could more clearly name my pieces like this
i could choose to make each of the parts completely separate each in its own module each small piece of this code is now clearly focused on only one part of the problem the code inside each piece is only related to that part and all of the code is related to that part so my code is considerably more cohesive than the design that i began with there's one part of this design that i personally rather dislike that doesn't separate concerns well enough for my taste that's here where we display the results this code would be much
nicer to my eyes if it didn't have the strings to be displayed in the same place as the anagram solving that would improve its separation of concerns and enhance its flexibility i left it like this because i wanted to make this point you can stop wherever you want if i don't care about for example internationalizing my code then i could stop here but if i do care i probably want to drive that from a test and that's going to allow me to check that my anagrams code doesn't care about the language that we are using
and that test will force me to separate this concern too the abstraction in my code is considerably better than before the anagram solver only cares about lists of words now not files on a special place on my disk and it's no longer directly coupled to the console either i could pass any function that takes a string now and it will be supplied with anagrams so we have improved abstraction and reduced coupling in the same step this is not just better tested codename this is better designed code as a result of these changes this code is
dramatically more flexible than before i can find anagrams in any list of words not just my dictionary i could easily imagine writing code that pulled a list of words from the web my anagram solver can work with that without any changes i can print to the console as before by passing the print function for my in for my display but equally i could write a display that rendered the results in a web page or hovering in 3d in virtual reality if i wanted because the design is better my code no longer cares i could even
change where the inputs came from allowing you to type the the entry into a console or maybe interpreting the spoken word okay i could have done that with the original version too but you can't blame me for trying these changes came not from me being some brilliant programmer but simply from the act of writing the test first and trying to make that process easy test driven development is the best talent amplifier that i know of and it gives us the earliest indication of when our design is poor that i know how to get if you
find it hard to write a test it's telling you that you need to do more work on your design by following the simple mantra of red green refactor our code is better we learn faster we make progress in more incremental more controlled manner our tests are more decoupled from the code that they test so they're more durable oh and as a nice side effect we end up with some handy unit tests too thank you very much for watching [Music] you