Robert Cecil Martin (uncle Bob) demonstrates test driven development by implementing a stack in Java

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
all right um I'm going to create a a new project called stack you and I are going to write a stack together this stack is going to be a stack of integers we will begin with a stack test so you can see I've created a junit test here called stack test I'm going to write a test inside it which I call nothing this allows me to wire up junit into the project good and now I should be able to run this test It All Passes I'm a programmer my nothing test works the first thing I do with every project every project Begins the same way because now I have an execution environment now what you and I are going to write is the class stack a stack is a first in last out data structure so the first thing you push in is the last thing that will come out it's going to be a stack of nothing but integers I'm not going to try and put generics in this thing we're not going to do that I'm not going to use any kind of data structure other than an array so we will work through the process of writing a stack and I'll begin that by writing the very first test now here's the problem what am I supposed to test I don't have any code what am I supposed to test and so here's the answer to this and this is always the answer you already know the code you want to write if you weren't doing test driven development you know exactly what you'd write you'd write public class stack okay so you just have to write the test that's going to force you to write the code you already know you want to write this is always the rule you write the test that forces you to write the code you already know you want to write I already know I want to write public class stack so what do I have what test do I have to write in order to force myself to write public class stack create the stack yes so okay fine I'll I'll change the name of this to uh create stack that will not be the name of this test in the end it's a reasonable name for the moment okay I want to create a stack yeah okay what should I call this stack uh my stack I'll call it my stack stack equals new my stack oh heavens heavens look at all the red that red that means it doesn't compile I've violated one of the laws I've got to stop and see if I can create the stack okay I'll have the IDE create the stack for me create glass stack yeah in the stack there it is oh good oh it all compiles and I'll bet it even runs damn a programmer all right I got that to work now now I can refactor I wrote a little test I got it to pass I can refactor this and there's really only one refactoring I want to do I don't want this to be called my stack I want it to be called stack I called it my stack because there's another class in the Java Library called stack and if I called this stack it would collide with it until I had established it in a package name stack and then everything's fine does this still pass oh yes it does good okay now notice that I'm not asserting anything that's a bit puzzling what should I assert about a brand new stack it's empty yeah okay fine so let's let's assert true that stack dot is empty no it happens it doesn't compile let's see assert true doesn't compile why well because you have to call that on the assert class okay well that's not LinkedIn okay let's Link in the assert class from org J unit good okay and oh heavens I don't have an is empty function all right so I guess I'll have to create is empty all right good um let's see it should return false that'll make it fail that's a good thing let's see if it fails yes it fails good now I need to make it pass and I can do that by returning true programmer now I want to refactor a little bit now by the way people don't like what I just did lots of people look at that and go that's stupid why'd you return a true there that's not the real code that goes there and my answer is well yeah that's true but I've just tested my test I've seen my test both pass and fail it took me no time at all why wouldn't I do this so of course I'm going to do that a little bit of refactoring I can get rid of that assert dot by doing a static import I think and there we go that should still work yep good okay oh I should change the name of this test all right uh what should the test be called um uh empty stack oh no new stack is empty good perfect okay next test oh pushing I gotta push something under the stack push okay uh what should I call the test push okay I'll change the name later oh I need a stack uh stack stack equals new stack oh heavens I've got duplicate code there look at that I've already got that line there and now I've got it here I'm gonna have to refactor that but not yet because I don't have a passing test can't refactor until you have a passing test all right I need to push stack dot push what should I push zero I'll push a zero all right ooh there is no push function look at that it's red all right we'll create the push function ooh that was weird yeah oh oh good yes all right uh void yes yeah it should return void I no no element I don't need anything else good um what should I assert here should not be empty good all right uh assert false uh stack dot is empty that's good okay um I don't have an assert false so let's see if I can get that that should be in the assert class I happen to know that okay oh yeah that seemed to work now I should be able to do the static import this should this should I'm not sure what it'll do oh it'll fail yeah it fails right okay because now it should not be empty okay how do I make this pass well I can't return a true there anymore yeah I could try that right if I could type false right but I think that's going to fail the other test yeah that fails the other test so now now I'm going to have to actually engage one or two brain cells here now one of the rules of test driven development is that you engage a few brain cells as possible because you're going to need them later right so don't do too much too fast so okay I'm going to have to do something here though and I think what I'm going to do is take that false and I'll turn it into a field of the class called is empty okay so now it's a field of the class that's good and I should probably initialize it here to be true because it starts out as empty I don't need this assignment statement there that I don't know how that got there I think that was the original initialization and now when I do a push I should say is empty equals false who test pass now there's already people in the room going this guy's an idiot what what is kind of gunky hold on hold on okay it's going to get more frustrating soon I've got some duplicate code here I need to deal with this duplicate code and the duplicate code is here and here I can deal with that by extracting the stack variable into a field of the class initialized in the setup method which my IDE will happily do for me so now I have a stack dot stack heavens that's unfortunate okay good there's there's my field of the test that holds the stack I've got a setup function that creates the stack that's nice I have my new stack is empty function which asserts that the stack is empty without bothering to create it that's nice and now I can eliminate that creation from that test I think that'll pass it does now I can change the name of this test to um after after one push is not empty okay and I think that'll still pass ah excellent good okay next test case so the way these tests run in junit is an an instance of the stack test class will be created the setup function will be called and then the first test function will be called then another instance of this of the stack test function will be created the setup function will be called and then the next test function will be called is that the question you were asking me okay all right so next test case yes pop yeah we could do that pop what should we test oh an empty stack see there's a QA mindset going on here right somebody is thinking uh I bet those programmers might pop an empty stack is a good idea so let's pop an empty stack what should it do if we pop an empty stack we should throw an exception sure we'll throw under throw we'll throw underflow when empty stack is popped good notice I wrote the name first now I kind of know what test I'm writing okay uh oh in order to make sure that it throws an underflow I can do this expected equals stack stack dot under flow dot class right that this now tells me what exception it should throw oh I don't have an underflow class well let's create that uh create the class underflow in the stack class so this is now an inner class of stat class I always like my exceptions to be inner inner classes of the class that throws them so this is stack dot underflow it should derive from an exception class so this should say extends uh runtime exception okay good I always like to extend runtime exception because I don't like the type checking on exceptions okay that's fine that's the that's the exception that we should be throwing now here we want to say stack.pop oh heavens I don't have a stack dot pop all right so I should create the pop function void No it should return an end uh okay zero I don't like zero let's return negative one okay run the test oh test fails why because it expected the exception all right I need to throw the exception well okay I can do that throw new underflow yeah okay oh heavens okay now I don't need the return this should pass ah pass good right next test case yes this case doesn't require that's true yeah yeah so there's a good question why did I why did I declare the pop function to return an in when the test didn't require it to return anything I I could have had the pop function return a void but I knew that it was going to return an INT in the end and I in grade I engaged one extra brain cell I figured I could afford it you know we're not really a hundred percent stupid here we just pretend to be so um okay next test case what's that pop okay is there something simpler one push one pop and the stack should be empty again that's simpler okay after one push and one pop will be empty stacked on it push zero stack dot pop assert true stack dot is empty look at that okay no it failed oh it threw an exception yeah that's right because I'm throwing an exception okay um when do I want to throw this exception only when the stack is empty so if it is empty then I can throw the exception otherwise I should return negative one okay now I can run the test test fails why um because the is empty failed didn't it let's see oh yeah right there that's what failed okay so now I have to make it um oh I think I could do this programmer now at this point most of you are going would you just write the damn stack sorry I can put bugs in my code anytime I want to put bugs in my house very easy yeah yeah it's so so it's a very interesting observation am I using any brain cells at all here or am I just making the tests pass by doing imbecilic things okay and I am I'm making the test pass by doing imbecilic things does this mean I'm adding bugs into my code yes this is empty flag it's just the wrong solution right it's the wrong solution we know that already so now at this point we engage a new brain cell and here's how we engage the new brain cell we say okay I know that this is empty flag let's look at it this thing right here that's the wrong solution what test can I write that will prove that that's the wrong solution push twice pop once perfect okay so uh oh don't you don't want to see my email do you all right so I've now engaged a strategic brain cell here this is a strategic test to force me to write a better solution because I know the current solution is wrong so test okay after two pushes and one pop will not be empty okay snack diet push zero stack dot push zero might as well push zero stack dot pop true assert false stack dot is empty huh this fails good how do I make that pass I gotta count it donate there's no way to do this I got to count it it's only going to be empty if the number of pushes and the number of pops are equal I could use two booleans this violates a rule there is a rule that this violates and the rule is that you are not allowed to make the the production code more specific than the tests I'm going to back up here just a second the the tests and the production code move in opposite directions every new test you add makes the tests more constrained more specific everything you do to the production code makes the production code more General that's a rule of test driven development so you must drive the two in the opposite direction if I use two booleans I would be making the production code more specific because I would know that I was doing that to pass one test I must make the production code more General how do I do that and you set a counter let's use the counter simple counter uh private and size equals zero it starts at zero that's fine um let's see empty should be size equals zero that should be fine okay when we push it we increment the size okay so I can do this size plus plus when we pop it we decrement the size so I should be able to do this minus minus size now I'm not using this empty flag I think I can take it away run my tests oh that was magic wasn't it all the tests are passing one simple little change made it a little more General that was magical okay next test Ah that's complicated a pop after all right all right I mean you know now there's another rule here and the reason I've been doing this is to emphasize these rules the the other rule here is that you don't go for the gold if you're writing a stack what is the golden behavior of a stack the central behavior of a St of the stack is first in last out and when you're doing test driven development you avoid the central Behavior for as long as you possibly can you don't run in and go for the gold you write tests for everything else around the outside first this is why you're so frustrated with me right because I'm sitting there going well what about the counter you know what about it's empty and not empty I'm doing all the stuff around the outside and you guys are going just right this dick okay prepare to be even more frustrated oh come on Bob Jazz good after pushing X will pop X stack dot push 99. why 99 I don't know it's easy to type sure it equals 99 stack dot pop oh see biggest shirt uh yeah okay good I can do the little static import good run the test and it fails because it was a negative one well I can make that pass okay but now knowing that I had done that I will augment the test ah okay okay now I got a problem I gotta save this thing that I pushed right I can't just return to 99. so I'm gonna have to save it I'm going to take this 99 here and I'm going to turn it into a field of the class called element good and there there it is up there that's nice I don't need to assign it in 99 that doesn't need to be there anymore I probably ought to initialize it to zero or maybe negative one would be better and then uh when I push it I should say um uh uh element ah yes this dot element meaning the field equals element meaning the argument hmm ass okay next test we're gonna have to go for the gold here now push twice pop twice yep that's it that's going for the gold that's going to be first in last out Behavior after pushing X and Y will pop y then X that's first and last out Behavior stack dot push 99 stack dot push 88 assert equals 88 stack dot pop assert equals 99 stack dot pop that's first in last out this is the first time we're actually testing stack like Behavior it'll fail ah now I'm going to have to write the stack okay wow one element is not sufficient I'm going to have to make it an array okay oh I should probably change the name to elements and I cannot initialize it to a negative one I have to initialize it to a new end of I don't need any more than two right now this does not compile but I can make it compile by putting brackets in there well I need something to put in the brackets and oh look at this I've got that huh let's just move that in there hmm okay and uh oh I need brackets there okay and I need something to put in the brackets oh I've got that let me just put that in there [Laughter] my test pass so damn you uh I've just written a stack and notice that all that code that I had surrounded it with all those tests forced me to build up that counter and it was the counter that was critical to doing first in last out did you know that that the counter was sufficient for first and last out maybe you knew that maybe you didn't but it became obvious at this point I had already implemented the counter long before I had done all the work for the stack this happens very frequently when you're doing test driven development as you avoid going for the gold you surround the entire problem with all this ancillary testing of all the interface and all the validation and all of a sudden you realize that you've done all the work and all you need to do is rearrange a few things and the gold just comes out of it almost for free you've seen a number of the of the bits of discipline here it would be a mistake to think that that's all there is to test driven development test driven development is actually a very rich topic there's lots and lots of stuff to do in this discipline that's just the first little bit of it and even that probably left you very frustrated like why would you ever do that but I've already given you the justification as to why so I think that problem is solved could you do this the answer to that is yes any programmer can learn this but beware if you decide one day that you are going to do test driven development at work you will fail you will fail spectacularly enough to convince yourself to never do test driven development again the reason for this is that test driven development is a rather significant skill it takes months to get enough skill to bring it to work safely so you cannot just leap in one day and say you know what we're doing test driven development and all of us are doing test driven development because it will be probably the worst week of your life what you have to do is learn the skill externally now how are you going to do that how are you going to learn the skill externally well maybe you can have lunchtime meetings where you practice this kind of thing or maybe you can just at lunch by yourself open up your laptop and do a little exercise there's loads and loads of exercises out there that you can do you can go search for code katas and tdd exercises there's tons of them and you should probably work them when you've got some spare time maybe you'll even do this at home professionals often take work home and work at home maybe you've got the time to do that I hope you do but do not take it to work and think you're going to make it work there learn it well first get good at it know that you are good at it and then bring it to work and you will find it to be very very rewarding at work once you're good at it there are lots of techniques that you need to learn lots of strategies that you need to learn it is not an insignificant skill so you are warned
Info
Channel: Agile Software Development
Views: 12,845
Rating: undefined out of 5
Keywords: testdrivendevelopment, tdd
Id: rdLO7pSVrMY
Channel Id: undefined
Length: 28min 46sec (1726 seconds)
Published: Wed Jan 18 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.