The Three Laws of TDD (Featuring Kotlin)

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
the talk I wanted to do is a talk I've done many many times before it it's called the three laws of test-driven development in which we are going to discuss the discipline of test-driven development and I will also do a small demo and I will use the Kotlin language for that demo three laws of TDD what is test-driven development well first and foremost test-driven development is a discipline and like all disciplines it has arbitrary elements to it that that many people might find frustrating or inexplicable so for example surgeons when they wash their hands before doing surgery they do not simply walk into the scrub room and wash their hands the way you or I might they have a discipline they follow a ritual and that ritual is fairly involved for example they they might get a brush and that brush they will use a certain kind of soap on and then once the brush is soaked up in their hands or are properly moistened they will stroke their fingers ten times on one side of the finger with the brush and then they will stroke that same finger across its top ten times and then the other side of the finger ten times and then the face of the finger ten times and then the nail ten times and then proceed to the next finger and you can imagine why they follow a ritual like this I want to make sure their hands are clean that all the bacteria have been removed but notice that there are arbitrary elements here is ten right number could it be eight should it be twelve do they have to divide their finger into the four quadrants or could they just do it with three quadrants there's this element of arbitrariness and that's also true of test-driven realm it's true of all disciplines all disciplines have this essential nature got to get your hands clean but there's also this arbitrary element ten times so test-driven development I have broken testament development up into three laws and those three laws are somewhat arbitrary but I believe they define the discipline pretty well these three laws seem stupid to most developers if you've been a developer for any length of time and and you start looking at these three logs you'd think well that's not I don't want to do that and as an example let me quote you the first law the first law of testable development says that you are not allowed to write any production code unless it is to make a failing unit test pass which means you must first write a unit test and that unit test must fail before you can write any production code now right away this just sounds dumb I mean what are you supposed to test there so cold there how can you write a test for something if something doesn't exist so that's the first arbitrary thing in test-driven development and it gets gets many developers put off and again make any sense out of that but the worst is yet to come because the second law makes things much worse it says you are not allowed to write more of a unit test that is sufficient to make that is sufficient to fail as soon as the unit test fails you have to stop writing it you can't write the whole test you have to stop writing it as soon as it fails and oh by the way failure to compile is failing so the instance the unit test doesn't compile properly you have to stop writing it and start writing production code now since there is no production code to begin with the unit test is probably not going to compile very quickly you're going to try two or three two or three words you may not even finish an entire line and you'll see a compiler error and you have to stop and start writing production code so this is much worse than just the first log and if you're a developer you think well how am I going to get anything done if I'm going to write a couple of characters or a test I'm gonna have to switch and write a couple of characters of production code but then the third block is send the worst of them all and it says you are not allowed to write any more production code then it's sufficient to pass the currently failing test now this is going to lock you into a cycle that is five seconds long you will have to write a unit test first because the first law says you have to but you're not going to be able to write much of it because it won't compile so then you'll have to write some production code but you can't read much of that because that'll make the unit test compile so you'll have to go back to the test and add a little bit more fat and that will work because it won't compile again you'll have to go back to the production code and you're going to be switching back between these two streams of code on a five second five five second basis and most developers especially if they have a lot of experience think of that as being crazy stupid boring tedious I mean you would never be able to finish an if statement or a while with statement you're never able to complete your thought you would always be interrupting yourself and that's a reasonable way to think about it it doesn't sound goofy it by the way sounds just as goofy to senior developers nowadays as washing hands founded to doctors in the early eighteen hundreds because back in those days no one understood germ theory and the idea of washing your hands seemed silly stupid boring tedious and a time waster took a very long time for surgeons to realize that pulling nails was actually kind of important so what if a group of us followed these three laws what would happen what if we had a room full of people and those people were following these three laws what would you observe about that roomful of people walk through that room pick any one of the people in that room following the three laws doesn't matter who you pick and it doesn't matter when you pick them everything they were working on executed and passed all its tests within the last minute and it doesn't matter who you pick and it doesn't matter when you pick them within the last minute everything that we're working on executed and passed all its debts what would your life be like if a minute ago everything worked and this was always true it was always true that everything worked and willing to go what would your life be like how much debugging would you do if everything worked a minute ago and the answer to that is fairly clear you wouldn't do an awful lot of debugging of evermore config oh it's not a lot to debug in a minutes worth of coding in fact the most common debugging technique amongst test-driven developers is to hit ctrl-v enough times to make the test pass again and then to rewrite the code you just wrote how many of you are good at the debugger how many of you have the hot keys in your fingers the debug flew in here in your body you know how to do breakpoints step into the step overs and you can set breakpoints them and watch points and you can really debug this is not a skill to be desired you don't want to be good at the debugger the only way you get good at the debugger is to spend a lot of time debugging and I don't really want you to spend a lot of time debugging so I want you to spend a lot of time coding now do I use a debugger yes I do I agree the one in IntelliJ is very nice I use it all the time but I don't use it for long periods of time and I don't use it as a part of my daily coding exercise the amount of time I spend in the debugger is is very small and usually it occurs when I've got some tests that's failing and I don't exactly know why and I set a breakpoint on the failing test and I step in a couple of times and it takes me right to the code and I see the fault immediately and go oh yeah and I fix it the amount of time I spend in the debugger is small enough that I can't remember which hotkeys are step over and step into at that seven and at base but I don't remember which one is which and I always have to hover over the little icon and remind myself it's just not in my memory that's the level of familiarity I I think you want to have with a debugger or something that you are tentative about rather than something more expert at now I don't want to tell you that you don't ever use the debugger I use a debugger as I just said and there are times when you have to do long orable debugging sequences because this is still software it's still hard weird things still happen but the incidence of that if you are a test-driven developer is much smaller than if you are not a test driven developer so I could tell you that you could cut your debug time by some factor call it D for debug time and I'm not even going to tell you what that factor is just D does this time that you are saving by not debugging make up for the cost and the silliness of the three laws hold that thought because there's something else to consider many of you have integrated a third-party package most of us have and when you integrate a third-party package you have to get it from somewhere it's probably a zip file or something you get this zip file and you unpack it and sure enough in the zip file there's there are some directories that have libraries in the Mabius class files or maybe it's jar files or maybe it's even source code somewhere in that directory structure there's going to be a PDF a manual written by a tech writer that describes the the way that this module works from how to integrate it and at the back end of this PDF manual there's an ugly appendix that contains all the code examples where's the first place you go well you're a programmer you don't want to read look tech writer road you want to see the code so you probably go right to the end of the manual find the code examples and and read them and understand them and that's how you learn to integrate the third-party package when you are following the three laws of test-driven development what you will produce in the unit test are the code examples for the entire system the same ones that you would have gone to at the back end of that PDF the code examples for the whole system that's what unit tests are they are little snippets of code that explain how certain parts of the system work and because you're doing test-driven development there is a snippet in there that explains how every part of the system works and these unit tests do not know about each other they are not interconnected unit tests do not form a system they are independent little units of code that reach into the production code and exercise a very limited part of it and therefore they are easy to read and easy to understand and if you want to know how to work some part of the production code there's a unit test that will tell you exactly what to do exactly how to call the functions what are you wants to call with exceptions to catch if you want to know how to create some object there's a unit test in there somewhere probably a set of unit tests that create that object every way it can be created and these documents that describe how to work the system because that's what these tests are there little documents are written in a language that you are intimately familiar with very comfortable with that language is so formal it executes it's utterly unambiguous and it can't get out of sync with the production code is the perfect kind of low-level documentation it's code and that's what programmers want to read so if you follow these three stupid laws you will inevitably produce a stream of documentation that covers the entire body of the production code in a perfect way for developers to interpret now does that make it worth to follow these three laws well hold on because there's another thing to consider probably many of you have written unit tests after the fact and if you have because it's the most common way of writing unit tests notice if you have you probably also discovered that it's not a lot of fun and and why isn't it fun why does look fun to write unit tests after the fact the answer that is pretty simple you already know the code works you already know the code works because you've tested it manually and now all you have to write a bunch of unit tests after the fact which seems like a waste of time because you already know the code works but you're supposed to do it because some process person said oh yes you must write unit tests and so you kind of have heartedly go in and write a unit test here and unit tests there and you look at the unit event sort of works you do another universe there that one works too inevitably however you will come through the code that's hard to test it's hard to test because you did not design it to be testable and the effort required to make it testable seems too large and you look at your watch and you say oh heavens I've got other things I've got to do and I already know this works I don't feel like spending the time in making this function testable and you walk away leaving a hole in the test week and you know that if you leave such holes in the test suite so do other people so you look at the rest of the team and you realize the rest of the team is also leaving holes in the test suite and therefore the test suite is full of holes now what is the result of a test suite the total hole when you run a test suite that's full of holes if that test suite passes it tells you nothing it does not tell you that the system works there is no decision you can make if that test suite passes if it fails it tells you something something something you tested broke and that's valuable but if the test suite passes it tells you nothing and of course you always want it passing so it constantly tells you nothing you don't trust the test suite how many of you have had this experience that you run the test suite if you pass and then you say the sort to yourself and say yeah it bad huh and you know that there's no action you can take just because it passed something different happens when you write your tests first when you write your tests first first of all it's fun why is it fun well fun because you write something that doesn't work and then you make it work and most of us programmers got into this business because once in our very young lives when we wrote something that didn't work and then we made it work and we felt the power and we want that power and that feeling again of constantly making things working every time you write a little game a test and you see it fail and then you - you get this little jolt of hope I'm a programmer feeling so it's fun or at least more fun than writing tests after the fact but but there's something else if you write the test first it is impossible to write the function that's hard to test you can't write the function to test because you wrote the test first you have to design all of your functions to be easy to test because you're writing with that first and a function that is easy to test as another attribute the reason it's easy to test is because it's decoupled that's what it wants to be easy to test something is easier to test if it's not coupled strongly into the rest of the system so by writing the tests first it's fun but you also produce production code that is much less coupled you will be couplet in ways that you never thought you would be complete but then something else happens and this is the real reason that we do test-driven development I'm about to tell you what that is all that other stuff the fact that you reduce your debug time and the fact that you produce documentation and the fact that you produce better code and it's fun all of that stuff that's why it's good we like it but the real reason that we do test driven development s driven development and your test suite has been produced by us driven development then when the tests pass it means something it means that the system works it means that you can ship you can deploy and that's really the goal of test-driven development the goal of test-driven development is to create a test suite that when it passes tells you that you can deploy there's nothing left to do nothing nothing no other tests that need to be run you are ready to deploy because the test suite how many of you I have looked at your screen and seen bad code there and had that bad code slowed you down how many of you have been slowed down by code that is a mess and at this point you're all nodding and saying well yeah I have and if you haven't been even you're not a programmer eagle-eye yourself because we have all been slowed down by that code the question then is why did we write it why did we write this code that's bad and the answer to that has a number of interesting factors to it number one we often write bad code because we're in a hurry we're in a rush we've got deadlines we have to go fast and so we write the thing that flows the sound in order to go fast I will leave you to deal with the logical inconsistency of that there's another reason why we write bad code and that's because it's impossible to write good code at first we're so focused on getting the problem solved that we solved the problem by virtually any means leaving a mess in our wake and then we do not go back and clean it up why don't we go back and clean up well for one thing it'd take a lot of time and we don't have that kind of got deadlines but there's another factor a much worse factor how many of you have brought code up on your screen you look at it you think oh god this is a mess I should clean it and your very next thought is I'm not touching it because you know if you touch it you will break it and if you break it it becomes yours so you walk away not cleaning the code I submit to you that if that is your reaction if you respond that way for the messy code on your screen then the only thing that could ever happen to that code is that it will rock it will get worse and worse and worse over time because no one can clean it everyone backs away and so the only thing you will ever do to it even though you're adding features and fixing bugs you will do so in a way that preserves your own safety knocks the cleanliness of the code you will respond in fear and you will rock that code and it will get worse and worse with time and as it gets worse and worse with time the whole team slows down because that bad code slows everyone down until you finally get to the point where the code is so bad and so awful that all the developers are demanding that the whole thing be redesigned but what if you had a test suite that you trust a test suite that covers everything a test suite that when you run it and it passes you feel confident you can deploy then you bring bad code up on your screen and you look at that bad code and your first thought is I should clean it and your next thought is that it could change the name of that variable right there and you do when you run the test suite and oh it passed by the way that touch leads gonna have to run fast so then you say oh that function is too large I think I'll split that function and - that'll clean it up a little bit you run attach all it passes and then you say oh I'm going to take that function down I'm going to move it to a different class and you run the test suite and it fails and you put that cup that function back and you realize what you did and you do it the right way in attachments this is the way we want you to behave we want you to have control over the source code we want you to be able to reach into that source code and clean it on a whim without a second thought and the reason you can do that is because you have that test suite and that test suite runs fast and the result of that test suite when it passes allows you to make decisions decisions like I will clean the code okay enough with my preaching I'll come back and preach them more at this point you probably want to see this in action if you've never seen it in action fine I will help you with that I'm going to do a little demo the demo will be in cotton so I'm going to create a cotton project here once I get my browser up there's intelligent good and I'm going to create a little project here we are going to do the prime factors kata this is one of my favorite demonstrations so I'm going to create a cotton module and let's see if I can remember how to do this oh yes that should be fine actors prime factors we'll call it comes my little workspace lovely and let's go here we're going to create a package named prime factors good and inside that package we're going to create a cotton file named prime factors test and over here you see I've got this little pop-up this is really important if you don't do this you're going to have trouble configuring your module what should do that by clicking on this little link here and it will pop up the module area here and this is sort of the runtime library is brought in and I I don't bother to copy it I just use the library from the plugin so I'll just do that that should make everything work just fine okay now I'm going to do prime factors cliff prime factors test look at that party we would have wanted to call it that's because of the name of the file I'm going to write a test with a unit test nothing now some of you may be looking at this and saying gee that looks a lot like J unit well yes it does look a lot like jannah' that's because it is changing Kotlin works very well with java and what we're actually doing here is we're using the the Java Janet library although notice that this is red here and the reason it's red is because I don't have the Janus library in my past so I'm going to go to look the modules here and go to my dependencies and I will go find my j-unit library which is somewhere up here in this horrible directory structure this is one of my complaints about IntelliJ I wish there was a better of directory browser oh yes here it is good last one and this one I'm going to take Janet and hamcrest both bring those in good lovely now it should know what this is does it import it yes it does it brought it in from Janet that's nice I should be able to run this and see how about that hmm little bit of build time and then boom we're up and we've got a test suite running this is the way I start every project the first thing I do and every project is get a nothing test to run now what are we writing we are writing a little tool that will calculate the prime factors of a of an integer so you handed an integer and it'll tell you what the prime factors of that injury are I wrote this long long ago because my son came home from school with homework and I wrote a little thing that would allow him to check his homework and I did it test driven and I used that analogy as this exercise so we are going to repeat what I did so long ago at my kitchen table to help myself of this homework I'm going to write a test called factors but I break a few rules here I'm going to write all the tests in a single test function as you get more and more familiar with test-driven development you'll realize that that's that's like the breaking of some of the rules it's a breach of etiquette but it makes this a little easier to read so I'm going to do it for your benefit okay I'm going to I'm going to invoke hamcrest here assert that a factors of one is okay the prime factors of one are not there are no prime factors of one because we don't count the one itself so this should be empty list and we're going to use the empty list function from the Kotlin library a bunch of red here let's get this straightened out we're going to put the appropriate prefix on assert that now we can import that jr. or Janet good now I can do the import static that's nice so I can create the factors of function which I will create right in here let's see call that end and I'm going to say that the list of int that's the integer in Kotlin silliness okay good so factors of will be a function that takes an integer in and produces a list of all the prime factors of that integer good and it's going I have to return something reasonable so we'll have it return an empty list good and our assertion here well is notice the little backticks that's there because is is actually a keyword in the language but you can use the back ticks to escape that keyword the is function comes from hand pressed so org hamcrest core matchers dot is is the name of this function and now I'm going to statically import that but I don't have to see that all the time so there we go it looks like it all compiles let's see if it runs Oh yep this runs good so I'm going to assert this e prime factors of one produces an empty list and that works because the main factors are function produces messy that the excellent so next step did that wrong the factors of two is okay and the prime factor of 2 is just 2 so it's going to be a list of two list of is a standard Kotlin function and in the library there produces a list with just a 2 in it kind of nice this should fail it does fail because we expected a list with a 2 in it but we got an empty list so this is the list that we expected this is the list that we got ok we can make that hat by taking this empty list and putting it in a variable named Oh factors and we will declare that variable with a law actually the bar Wow the difference between a valid of ours that valves are immutable and VARs are mutable and our list our factors list is oddly going to be immutable even though we can add things to the list so we'll come to that later you'll see it happen shortly now I have a list of factors and in order to get to to be produced I need to say something like this if n is greater than 1 well then it's probably - because that's the only test I've got factors dot add - this looks just like Java that's because it pretty much is Java but notice the add doesn't compile why not well because empty list produces in an immutable list a list that can't be changed and so the compiler says oh no you can't add to a list that can't to change I can fix that by returning a mutable list oh nothing still an empty list but now it's mutable notice that makes it compile I believe this will pass that's excellent I've made it pass lovely I'm a programmer [Music] all right let's do three factors of three oh three is a list of three well this is really easy to make pass although it might seem difficult at first it turns out to be trivial because you can take that two and turn it into an N and if you think about that for a minute so realize that will pass all the existing tests okay clink next factors of four is a list of two and two okay well how we get back to pass I think I can do it this way inside this if statement yes n is divisible by two then what we want to do is put a Q in the list no semicolon and we want to reduce n by two by a factor of two so n divide equals two and that would take our four the four that we're passing in this will take our four our 4 is divisible by two little pro to do in the list it'll turn that 4 into a two and then it will put the other two in the list so that should work just fine except that it won't compile now I want to compile well it doesn't compile because arguments to functions are not mutable and I'm trying to mutate it here and it would be interesting if I could do this to say that the argument is mutable but the language doesn't seem to allow that maybe there's some trick I don't know yet so what I'm going to have to do is introduce a new variable I'm going to do that this way I'm going to change the name of the argument temporarily to mmm then I'm going to say are N equals n n that will create a variable something that is mutable and now all these references n are mutable you can see they're mutable because they're underlined IntelliJ has this nice thing of underlining everything that's mutable this should work Oh having two fails oh but look at why it failed this is the to case this is this test here that's failing and it's failing because it expected a list with a two but it got a list with a 2 and a 1 where did that one come from well that one came from right there right because we took the two in oh that's divisible by 2 we reduce the to 2 and n acute of 201 and we put the 1 on the list so we don't want to put the 1 on the list do that by saying only put in in the list if it's greater than 1 all right that should pass but we've made a bit of a mess here we need to refactor a bit and then and in a terrible name so what is this end well this n is the remainder after you remove all the factors so let's change that to a remainder that's nice and now we can take take our end and we'll just make that n again because it's the number where that's coming in to be factored and I believe that will still work oh good and yeah ok oh look at this if statement here this if statement has the same predicate as that's the if statement and it comes right after it that's weird isn't it it kind of looks like there ought to be some way to combine those two I can actually make it weirder by taking the if statement and moving it outside on the first one entirely this one is still passed think about why it still passes but now notice I've got two as statements at exactly the same level that have exactly the same predicate in them that's interesting why is it interesting well you're about to see why because first of all we're going to do a few tests and you'll find this interesting if you've never seen this before the factors of 5 are a lift with five in it excuse me list of and this will pass because five is prime weed is not divisible by 2 so we fall through right to here and we add it ok so uh back factors of six is a list of two and three list of I'll get that right this will fail don't know it'll pass won't it it will pass because 6 is divisible by 2 and there will be a three left over ok FFF factors of 7 is list of just 7 and that will pass because 7 is prime so lovely good three in a row guys three in a row you know where this algorithm come from and if I've already done a whole bunch of them it can factor from 1 up to 7 already sees that factors of 8 is a list of ok who invented all right that's going to break everything isn't it because now we've got three factors and nothing we've done can put three things in the list how do we get this one to pass and here's where life gets very interesting because we can take that if the one that makes it pass we continue to factor out dudes as long as it's divisible by two and that makes it pass it was an interesting hmm okay so now let's unite the factors of 9 left of 3 & 3 those are the two prime factors of 9 this will fail because we don't factor out 3 is yet now I can factor out 3 is by taking this loop right here and copying it oh yeah dog ok there we go good copy it and then taking all of the twos and turning them into 3 and that should factor out all the three that works it's also hideous terrible way to solve this problem if I solved it this way I've never I've never completely solved the problem so I've got to get rid of this although it gives me the clue of how to really solve it but I really need to do is take this while loop here and put it into another loop before I can do that though I've got to take that to turn it into a variable all three instances please which I will call divisor plane and I will take divisor up out of the loop I will now turn this if statement get ready for this you're going to really like this I will take that if statement and turn it into a wild statement because I want to continue looping until the remainder has been reduced to one right you divide through by all the factors you're going to get a one in the end aren't you but I better increment that divisor I'll be back here and oh oh I can't increment the divisor because it's a vowel I got to make it a bar there we go notice it underlined it nice run my test oh that's bad and what is the terminating condition of this while loop is when remainder is equal to one that's what this is going to do it's going to turn remainder equal to a 1 which means this this statement will never get execute it's really the terminating condition of the old while loop excellent good gosh I think we have an algorithm don't wait let's see if this algorithm works the factors of Q times 2 times 3 times 3 times 5 times 7 times 11 times 11 times 13 I take that correctly good yes is a queue joe mcusic on a 3 comma 3 comma 5 comma 7 comma 11 comma 11 comma 13 type that correctly looks like good hoop I forgot the list oh yeah is a list is the list of good yeah so yeah good let's just line these up cheese will see they're the same looks the same if I have 7 11 11 13 yep does that work and of course it does we are done with the algorithm now the interesting question here is where the heck did that algorithm comes up but I didn't design it upfront I didn't think it through outside and plan out a way to solve this algorithm what I did is I just posed a bunch of test cases and one by one I made those test cases pass and somehow this algorithm came out I'll leave you to ponder that it's one of the more interesting elements of test-driven development apparently test-driven development is a way to incremental e derive solutions to problems that doesn't mean we don't plan things doesn't mean we don't design things doesn't mean we don't do architecture it does mean however that we've got a tool or a discipline that supports incremental development of algorithms and with that I'm going to stop my talk in my demonstration and open this up to questions because I see there are a bunch of questions on the list and I believe my lucky assistants are going to be picking those those questions for me so what questions you have an answer okay so the first one is how would you approach this problem is being asked how to use TDD along with randomized testing or even more general approaches by property or model-based testing so in functional languages there are these interesting test frameworks that allow you to specify a very rigorous tight structure and then the testing tool will invent random values that fit within to that type structure and that constrains the number of values that the tests can then feed into your application and your application should be able to deal with them all very useful tools if you've got a tool like that like FS check or quick check or something like that I strongly urge you to learn those tools and take advantage of them because they're very powerful is that what I'm doing here no I don't have any randomizer here could I have invented a primitive randomizer in order to do some of these tests for the prime factors problem probably not but for other problems where the the value of a number is not quite so important as the fact that it is a number or the value of a set of fields is not quite so important as the structure is then I have done some primitive random creations that can be useful sometimes and then you can iterate like a hundred times you can say give me a hundred instances of random values and I'll plug them into my unit test they're still unit tests there's still property tests and we some people try to make the the distinction between unit tests and property tests I look at them and say well I property test is really just a unit test that accepts a bunch of values so I think that's a perfectly valid thing to do and you should learn how to do it next question from Dave no problem and what you say but sometimes you start working on an existing code base and want to improve it do you use some of the tactics from Michael feathers I do and so Michael feathers wrote a book and the name of that book is working effectively with legacy code one of the most depressing books you you might ever read because it's it makes it very clear that there is no easy solution to this problem no quick fix for legacy code it is very difficult to take a very large wad of legacy code and bring it under the kind of control the test-driven development would give you it is a long and a frustrating task but doesn't mean it's not worth doing I just want to make sure you understand that there is no quick fix here to get a bunch of legacy code under the kind of control the test-driven development will give you is a pass of months and probably years the benefit however is that as you are as you are gaining more and more control over this code your life does get incrementally better and better so you may not be able to run the whole test suite and Trust it but you will eventually get to the puck to get the point where you can run segments of the test suite and trust that there's a whole discipline for doing this or taking a lot of legacy code and gradually getting it under control and Michael's book is a great introduction to that particular discipline there are other people who have done talks on this I have done talks on this it's probably not appropriate in this minimum amount of time that we have here to go into that discipline suffice it to say however that there is a discipline that will help you get through the legacy code issue okay so from Michael Mario he says you're talking you're taking the test should run fast and this is what everyone was saying for many years but now a lot of developers are moving from fast class level tests to more like functional tests of starting embedded web containers and testing rest api those tests check much more out at the same time much smaller what is your take on that and this I think goes in line with someone else that is also asking saying do consider long compilation times an obstacle for TDD they're both somewhat talking about speed yes so long test times and long compile times long build guns are obstacles to test driven development and their obstacles to software engineering in general any time you put a long lag into a feedback loop you lose control of the thing that you're trying to control it's just the way control theory works our goal as software developers should be to eliminate or drastically reduce any of those lag times in the feedback loop so tests need to run fast I understand that people try to do in a long testing through REST API there's nothing wrong with testing a REST API the the fault of the the mistake that people make is that they test business rules through the REST API when you're going through a REST API what you should be testing is that the API communicates properly with the internal module and then run a series of unit tests on the internal module if that's all the business rules and those can be done fast any test that slows you down needs to be looked at and addressed so for example um you don't need to test a REST API each fat each faction of the REST API more than once once you know that the API can take apart see the rest strings and identify the variables and so on once it can convert that HTTP request into a data structure you know that that REST API is working as an interface you don't need to test that more than once then you can write unit tests that test the individual functions inside your module if you test all your business rules through the REST API you are executing all of that conversion code and all that socket management code and all the the HTTP request response cycle over and over and over again worthlessly don't do that there's a couple of questions in line with mock objects and mockito are not demanding and they're all basically asking the same thing like what is your take on mock objects and how do you do TDD with mocks or using platforms like mojito so as well I love the Marvel tools I think they're very very powerful and very interesting I have used Makita and Jayma and easy mock and lots of other other tools like that what I find however is that for most of the mocking I do I write my own it's especially if you're using IntelliJ it's just remarkably easy to point at a Java interface or or Kotlin interface and just say generate it for and it'll automatically generate it as a nice little stub and then you can reach into that stub and add just the right code you want and you don't have to put the horrible dots and parentheses kund of thing that you lock or mockito forces you to do nothing wrong with that it's just that having a nice little class that I wrote myself in a directory and it's got a nice name and it's easy to create is my preference I grab it do that is mocking important yes mocking is important especially as the architectural boundaries of the system and I'll say this and I won't justify it and you can do some more research on it on your own later I prefer property based or value based testing inside a module and mock based testing across boundaries there are some people who want to do mock based testing across everything every function is mark I don't do that I do value based testing property based testing inside modules and I use mocks across boundaries and by the way those mark speed everything up enormous right next question Brandon asked recently can Beck wrote a tweet hinting that it's foolish to be proud of 100% test coverage he compared it to being proud of reading every word in the newspaper it sounds I can't believe TD can be done without 100% test coverage do you agree with Kent I agree with chance to the extent that a hundred percent test coverage is an impossible goal to achieve or or a virtually impossible goal to achieve I do not agree with Kent in his analogy his analogy is that that writing 100% test coverage is equivalent to being proud of the fact that you read every word in the newspaper well you don't need to read every word in this state there's only certain articles you're really interested in but you do need to know that everything in your code works so the analogy breaks down pretty rapidly now do I tasks everything with unit tests so the answer to that is no I can't test everything with unit tests there are a whole bunch of of behaviors in systems that are not immediately testable with other code and the obvious example of that is stuff on the GUI it's very difficult if not impossible to write a unit test that makes sure that you have displayed something properly on a screen that doesn't really I don't test it I just have to test it with my eyes my eyes become the unit test I am very careful however to separate the things that can be tested from the things that are very difficult to test and there is a a pattern here that is very helpful is called the humble object pattern if you can look this up on the web sometime if you'd like to the humble object pattern is the way that we separate the things that are testable from the things that are difficult to test and they typically occur at physical boundaries to the system on the screen or add a device driver or add up some other kind of physical boundaries is where it really gets difficult it's also difficult to test things where you don't know the answer up front sometimes you write code that's entirely speculative and there's no way to write the test first because you don't know what the answer is so then you can't write the test you have to write the code first and sometimes you're going to write a test after the fact now having said all that I will come back to the point and say that the vast majority of the code that you write you do know the answer to and it's not hard to test and you can write those unit tests and for all that code that you can test you should one more point I do not test every single line of code explicitly quite a bit of code can be tested indirectly so for example getters and setters getters and setters do not need independent test because the getters and setters will be called inside functions that I am testing so they get tested they just get tested indirectly and that is sufficient for me okay so we there's a lot of questions unfortunately we're not going to have time for all of them but there is one that stands out and I guess it's somehow related to your recent blog post and he says it's by Nick harmony asks you stated that a compilation error is also a failure in calling trying to access a property of something that's not able you will get a compilation error what is your final decision on features like null safety and cotton it does take away the need for writing some tests the language tests it for you oh so this is a very long discussion when I can start that first of all I wrote a blog because I was I was concerned about the trend in modern languages languages like Kotlin and Swift and there are many others which seem to be in crunchings ever further into static type checking and I think that's a trap and the reason it's a trap is that static type checking couples you to the compiler the code is deep the more you statically typed check the more deeply coupled you are to this compile time checking and you are you are coupled to the invocation of the compiler across module boundaries it's very difficult in a strong attack statically type system to create a small set of in it with independently deployable modules if you change one of the types you have to recompile everything and that becomes a bit of a problem so I think we are moving and maybe have moved across a balance point where we're going to have to pull back from again now he said something else which is that static type checking means there are certain tests you don't have to write right it seems that way I understand why you would say something like that I do not find that to be correct however because when you're writing unit tests you are not testing types you are not testing the you pass an inch into a into a function you are not testing that you pass an employee data structure into a function what you are testing instead is a behavior of the system that the function operates on that employee properly or operates on that Inc properly and that is not something that a type can specify a type specifies a constraint in the format or structure of the data but not on the operation and unit tests are always tests of operations so in a unit testing environment if you test all the operations you are indirectly testing all the types but you don't explicitly just show so I don't believe there are any tests that you can avoid by adding stronger and stronger static typing to your languages you will still have to write all the unit tests you would otherwise I've had to write may I have a follow-up question to this so one of the big points you're making and I'm just not getting it is that whenever you change a type somewhere you have to recompile everything because the interface in your models module changes is that correct yeah so but if the type there makes any sense this is what you have to do because all the other parts of the system have to encounter binding have to account for this contract that has changed so it's inevitable anyway it is inevitable that two modules that share a type have to agree that's what's allowable if the compiler enforces that in agreement then the two modules must be compiled if the two much the compiler does not enforce that agreement it is possible to write code in one module that makes the agreement possible dynamic type checking and by the way I'm not I'm never opposed to type checking try checking is a good idea my F my problem is where the type checking occurs does it occur in the compiler or does occur a long time and certain amount of compile time checking is a good idea too much cause of this compile time couple if we if we check our typeset run time some of the really interesting details of our types of run time we can get away from having to recompile the world whenever a type changes well to be fair we they rarely recompile the world or the type changes we often recompile even your clients if your campari compiling if you if you can isolate the boundary of types so the types only exist within certain modules and modules don't share those types then you can enforce that type safety a great deal because you're going to be recompiling that module anyway if however the types span many multiple module pounders if the mod casino if the types are known to many different modules then a change in type causes a complete rebuild and a complete rebuild means a complete redeployment and I would like to avoid that I don't want to be doing massive redeployment one of the reasons that people got really enthusiastic about microservices is that microservices create this blockage in the type checking wall when you communicate from one micro service to another all the types disappear and you're suddenly talking across this REST API which is essentially a string and it's all dynamically typed and then of course on the other side you reconstruct the data that you wanted and everybody got very excited about that because it's decoupled and it's independently deployable what they didn't realize is what they were doing was simply trying to escape from the type checker and that you you don't have to do it in micro services if you've got a language which is somewhat forgiving with types this is a very interesting and very long-winded philosophical argument on where our languages ought to be and it is my opinion that we do not want to push our languages too far down the static typing ro okay yeah since I'm anyway here I totally agree that it's largely a matter of opinion and yeah there are people who advocate for Less sites and for more types and the balance point is somewhere out there and nobody can be sure where that is so it's like every person's intuition as if where it actually is if you take the last 40 years and you integrate it you will see that the type the type checking of languages oscillates back and forth across the point then you go from from C algal to feed a C++ to Java to Pascal all of these languages oscillate back and forth and the reason they oscillate is because if there are bad things that happen on either side of the center point and so there's this reaction oh there's too many types we need Ruby oh there's too few types we need Swift and we keep on going back and forth across this point and my blog was an observation that the pendulum has swung again and it's past the center point and we're going off into the again not for the first time but again into the strong static typing realm which we will have to pull back from because it's painful yeah well I can plan and I think that there are actually languages at far besides at the balance point like there are actually languages where you don't need to be any Destin because they allow you to verify the behavior with the types dependency types functional languages but those are so far from what anyone would like to program that we are not even considering that on the mainstream yeah those languages become turing-complete at compile time and then you have to wonder well okay what what is the execution platform is compiler the execution platform or or do we want to actually execute in the running machine it's very often actually the other way around they become curing incomplete at the runtime because they have to limit the power of the formula the type so that they terminate they are often curing incomplete indeed there are variations where for smuggle the to incompleteness back in the computer has become Turing incomplete the compiler is now too recently what a would I what I want to tell it all depends under what you what you want to spend your money on like it really sure it works correctly or being able to do anything you might not need actually to do okay so we're past the hour I personally I find this discussion very interesting and Uncle Bob if you ever want I think it would be fantastic to have you and Andrzej on another webinar discussing some of these points so you know the door is always open for you whenever you like so I always need to do that awesome so we'll try and schedule that for for sometime in the future but we are past the hour and we don't want to keep you avoiding so I very much want to thank you for coming on and for everyone hanging out and let me just switch over to just put on the final slide and I'll take the time to say thank you to Uncle Bob it was very interesting both the presentations description thank you very much I had a lot of thank you thank you very much uncle Bob and I'm doing food for being on so if for everyone else we will be recording this well it has been recorded and we'll be publishing it on Jerry's TV on YouTube and if you signed up when you register we'll be sending you the link so once again thank you very much for doing the presentation on kebab and further one farming intended
Info
Channel: IntelliJ IDEA by JetBrains
Views: 185,821
Rating: 4.8933902 out of 5
Keywords: TDD, JetBrains, IntelliJ IDEA, Uncle Bob, Java, Kotlin, test driven development, uncle bob martin, test driven development java, uncle bob tdd, tdd java, robert c martin, bob martin, tdd tutorial, robert martin, kotlin tutorial
Id: qkblc5WRn-U
Channel Id: undefined
Length: 66min 8sec (3968 seconds)
Published: Fri Jan 27 2017
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.