[C#] TDD and the String Calculator Kata

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
all right so we should be live now and today this evening i'm going to be working on tdd and the string calculator cutter let me just do a quick sound check i'm going to be working on all right that sounds good i can shut that down let's uh hopefully this has been on the screen long enough that youtube picks it up when i put it there so jump here and say welcome uh this is part of the devbetter.com exercise we're doing this week i wanted all the members that have time to go through the string calculator kata you can learn more about dev better by going to devbetter.com it's a coaching program i have for software developers get rid of that uh if you're not familiar with the string calculator kata it was uh created by roy oshirov a long time ago 10 years ago at least and it's it's a nice kata you can do to help with test driven development and that's what we're going to do here this evening so i have a list of katas out here on github.com catalog and let me throw a link to that in the chat for anyone who's interested so there's a bunch there these are things i've done with various user groups and things like that for many years and and they're very useful if you're not super comfortable with test driven development or you're trying to learn a new language or practice a design pattern or refactoring they have different strengths and and variety so you know you can kind of go through this catalog and see if any of them really strike you as being something you'd like to do they can be great for a lunch and learn or at your next.net user group or or what have you alright so the string calculator is just one of them here we'll jump to it and the instructions for it are shown here and i'll make this a little bigger for you um and the idea with this kata um is it's just an exercise to help you get better writing writing code writing tests um roy oshirov wrote the book the art of unit testing i think there's a second edition out now and so this was something to help people get better at writing unites katalog i know i i really should have just called it catalog mcnerdis that that's been frequent feedback in fact probably there's an issue here that says that i should rename it um in any case it is what it is i think people might search for this more uh more more readily we'll see okay so the thing that makes this kata different from a lot of the other ones in this in this kata log is that you want to try and do it within a 30 minute period now that's not required you could certainly just take as long as you need get through it all um you know and enjoy the fact that you completed it and that's fine but if you're trying to see uh how well you can do in a certain period of time you want to be able to repeat this and see your progress because again the idea here is that you want this to be natural you want to develop muscle memory you want to be good with the tools and the keystrokes the commands whatever it is that makes you be more effective at this so that you can take that and use it at work use it when you're building real production software when you might be under time pressure uh as we so often are so the idea here is do do it and see how far you get and then just stop uh and then wait however long you know don't don't start another one immediately necessarily you can i mean it's not against the rules but you know give it a month and come back to it and then see how you do a month later and see if you've gotten any better right maybe you're doing some other katas in between or doing some other ways to practice now i haven't done this kata in at least a year i would say although i have done it uh probably half a dozen times in the past uh maybe maybe a couple more um and so one of the things about this that also makes it a little bit different than some of the other exercises is that the steps should be done incrementally and you shouldn't read ahead to see what the next one is so yeah the idea here is try not to read ahead and guess what the next requirements might be just work on one step at a time so we're going to read the first step here before i start my timer and just read all of section one's requirements which are now on the screen so the goal here is to create a string calculator that's going to be the name of our class and it has a method called add and it takes a an argument called numbers of type string so that's why it's a string calculator and this returns an integer now it's even going to tell us how we should start writing our tests it says start with the simplest test case that says if there's an empty string what should happen um if there's one number what should happen if there's two numbers what should happen try and solve things as simply as possible we'll see what that means in a minute but with tdd the idea is that you want to do the simplest thing that could possibly work to make the test pass even if you know that's not going to be the code that you're going to end up with all right so then on step 3 it tells you that if there is an empty string the sum should be zero right we're not going to throw exceptions we're not doing any guard clauses or validation here if we if we get a string with that's empty we we will sum that up to zero numbers can currently include zero one or two integers and the example input shows them as you know empty string one one comma two uh address should return the sum of the integers provided in that string and then we want to refactor after each test so we want to follow red green refactor and in fact we're going to do a slight twist on that which is rgrc which is red green refactor commit so i've got this going into a git repo and there's a really nice tool you can use called get history.xyz that will let you kind of step through your commits and it has an animated view in the browser that shows you the the history of what your code looks like so it's nice to see what things look like when you're refactoring and in fact you can even commit more frequently you can commit uh you know red green commit refactor commit is sometimes even better if that doesn't get to be too much all right so let's start with our project i have a 30 minute timer here and if you didn't know if you weren't uh sure the easiest way to get a timer turns out google is really easy so you know you can set this timer up set it to whatever time you want if we want to make it a three second timer just so you can see what this thing does when it finishes it makes a sound right so then you know that it's done so let me just reset that and the other thing is of course the time doesn't start until you're ready to go with your environment setup and all that stuff so i've already created my repository that i want to commit this stuff to i did create a solution using a little bit of net cli here so i said when i finally got the syntax right it was right here dot net new sln we'll create a new solution uh dash n is the name and this will be the string calcutta 20 2105. i don't expect to do any more in this month and this is going into a new github repo i created called string calculator attempts so i could put multiple different attempts in here strangely enough i don't already have a bunch of my past attempts in github for for some reason i try to put everything in github but we're going to start now all right so i've got this string calculator dot sln created we're going to go ahead and work in visual studio today so it looks like this i don't even have a project in here yet i've just created that empty solution so the first thing we're going to do is we're going to add a new project and for this we're going to use a x unit test project next and we'll call it string calc what do i call the solution i can't remember now string calc uh 20 21.05 that'll work um that goes in there should be fine i hope it puts it in a subfolder i can never remember what it's going to do but we'll see what it does all right so create this project now because it's a kata we don't need multiple projects we don't need a source project and a test project and all that we're going to do all our work in just this one place and we know that the thing we're going to write the test for is called string calculator so we can just start with that and technically maybe i should be starting a timer right about now but i'm going to get myself at least set up with an empty class and an empty file and we'll move that to string calculator and then this thing has one method called add and i prefer to name my tests i test classes to be the name of the class i'm testing screen calculator if i could type add and i go back and forth on whether or not to use an underscore but we'll just say string calculator add is the name of our class and here's our first test which i'm not going to start doing anything with until we start the timer now how are we going to run these tests and how do we want to see these things at the same time let's take this and before we start the timer let's just rename our file there we go and then we can open up string calculator and then it's up to you whether you prefer your test on the right uh or the left i think i like the i think i like them the other way so we're going to go put this over here and put this over here and then there's our test explorer which i'm probably not going to use since i have the version that supports it i may just use live unit testing because it's faster but occasionally i may jump over here into test explorer and just see these things running if you haven't used live unit testing you can go in here if you have the right version of visual studio and you can just say start and you'll start running any tests that it discovers and we'll get rid of that all right so that's what this little thing here is is doing is it's looking for live unit tests which right now it says excluded i don't know why um where's live unit testing should be in here somewhere right i don't see it oh there it is okay include so include all the things and now there's a test running and it's green so that means it ran um okay now let's start the um timer all right so let's jump back here we'll hit start we go back to our instructions here the clock is ticking simplest test is an empty string empty string should return zero all right so that means we want a test that says add returns zero given empty string okay before i get much further than that i want to do git initial commit 2021-05 commit all the things and we've got that going all right now i don't need this in my way anymore we're going to say var calculator equals new string calculator all right and then we'll say our result equals calculator dot add empty string okay and then we'll say assert equal zero result now here's where you could decide you want to use fluent assertions or some other assertion framework now this thing doesn't exist yet so we're going to generate it it's not implemented and live unit testing shows me that my test is failing right i want to make this pass with the simplest thing i can i'm going to call this string numbers because that's our requirement and we're going to say return 0. now we've just done the first test now we're going to commit real quick and say test one commit and get that out of there okay so now we need to do another test the easiest way to do another test is to copy paste because copy paste programming is the best we're gonna say returns one given a string with one and we do that and we do that and guess what it fails it fails and we can say if string dot is we'll say null or empty which is a little bit more than we need numbers then return zero otherwise return one and once again we're green now at this point we might want to refactor um because we do have a little bit of uh hey sean how's it going uh we do have a little bit of duplication here i generally follow a three strikes you're out rule for duplication so when i see duplication twice i just go with it by the time i get to the third time then i want to do something about it now the next requirement was going to be that we have this one number or we have a comma separated number i don't think we've covered enough of the cases for the one number yet so i'm going to write another test and in fact i'm going to use a theory at this point and say inline data this is the next unit feature that we're going to pass it one and we'll just say the expected value is one and we'll put that in here and we'll say string numbers expected result and then we just change this to be the numbers and this to be the expected result and it should still do the same exact thing so it's not it's basically a refactoring right so let's go say we did test two plus refactor to theory there's that all right so now this becomes test three when we say well what happens if we pass you a two we expect a two all right now we fail again because this thing is just stupid um so what would be an easy way to make this now more generic more flexible well we could just say it does inta parse numbers and i think that will make it pass and it does so we'll do that should we refactor well there's still only two of anything in here that that's the same so i think we're okay we'll come in here and we said test three and commit and continue on so i'm done with this type of case i should probably rename this returns uh number given string with one number okay so what should it do if it has two numbers well we'll keep the theory this time because there might be some other things we want to test but we'll start with 1 comma 2 because that was given to us and there's 3 right and so we want this to return sum given string with two comma separated numbers like that and what does it give us well it doesn't give us that so over here one two makes three and just finished two of steve's katrina me oh pluralsight courses nice cool cool cool all right getting distracted by comments sorry okay so how do we do this in the simplest possible way right we can't just in that parse it um now we can be a little bit goofy here just say something like if numbers dot contains you know comma uh return three right and that gets us through it for one but that's really sort of a hack so let's let's take that a step further and let's say what happens if it's not those two numbers right if it's two and three and we're going to get 5. okay well now we're forced to do something with this so a reasonable thing that we might do is split it right so if we say var inputs equals numbers dot split on comma right now i have a bunch of characters uh there okay and then i could say and those are those are strings right with a bunch of strings in an array but i could actually make this the result at this point because i i know link and i know i can take a bunch of strings and i can do an in-depth parse on them so i can say with link something like dot select which i need to pull in link for and dot select s such that in dot parse s right uh now i've got what i've got a bunch of uh numbers right out is an int or a sequence events and then link is nice because it just gives us a sum so we can do that and then here we just say return result and i need a parenthesis for that to build uh and we're good right everything's green again now at this point we can check in because everything's green so we'll say this is test multi numbers commit but it's probably a good time to think about refactoring here so we've got string calculator string calculator string calculator new is glue and it's binding my test to this class just as much as it would in my production code so i'm going to say private string calculator underscore calculator equals new string calculator and if we're going to have a bunch of different ways to build it i'd use a builder or something similar but in this case it's it's going to be pretty trivial so we're going to do that and we're going to do that and we're going to do that and get rid of a couple more lines um okay so that's good now we could also get rid of this line var result equals whatever with some thing um for now i'm going to keep that i think this is easy enough to read and it's only two lines of code so i don't need to be that small so let's keep that the way it is uh and i think we're ready to go read the next step so i don't know how much time i've used eight minutes and we're done with step one i think right we've done these three cases we've got the sum we've refactored all right now step two not reading ahead to step three if i can help it says allow the add method to handle an unknown number of numbers in the string all right so that's fine let's do uh we had two let's try it with three just in case somehow that's different i think my solution already works but let's do three comma separated things and we'll say one two three and two three four and that makes six and that makes i don't know tough math there and we're still good so uh this is finished three numbers step two done all right what next alt tab to the thing there we go allow the add method to handle new lines between numbers as well as commas for example one backslash n comma return six um one comma of x plus n is invalid but you don't have to test for it so no no need to even write that test so back here we want to be able to support new lines so let's start with taking an existing where you read taking an existing test and getting it to work with new lines so how about this one and we'll bring it down here um let me see what do they want it to be one okay no i shouldn't do that one let's do this one because it's got three numbers and bring it here so there's ourselves three comma or new line separated numbers i'll get rid of that and the actual input they want was backslash n and n comma so right here exactly what they gave us uh and look it's red it doesn't work so how do we make that work over here well we need to split on something that isn't just one of those and it turns out split has some options i believe it will split on multiple different things um or there is some type of thing that will split on multiple things so let's do uh care delimiters equals new care and we have a comma comma and we have backslash n something like that is that valid no i'm gonna do that all right this should be var there that passes okay so now i've got the delimiters um and the split take delimiters can i just do this does that compile yes look at that all right so um things are looking up we've got everything good can i refactor this um i think i'm still happy with two lines per test so but let's just test a few more cases just to be fair uh what if we do two backslash ends what if we do uh backslash what if we do comma backslash n all right i know these all should work but they all do so there we go so what was that that was step three done so step three done bam all right now we come back here and we go to step four which says allow the add method to handle a different delimiter to change the delimiter the beginning of the string should be a separate line formatted like slash slash delimiter then a backslash n and then all the numbers for example whack semicolon lets us just use a new delimiter of semicolon um i don't remember if that's in addition to all right the first line is optional everything else should still work as before okay so let's write a test for that in our test uh you should test one comma nope it said not to helpful stranger uh i was going to i really would but it told me not to do it all right so in here now i want to say basically the thing they gave me which is this and we'll put that there so whack semicolon backslash n 1 semicolon 2 semicolon 3 should work right so return some given string with custom delimiter like that all right so that is a test that i think should work one two three make six there's my delimiter there it is right there all right so over here now you want to check and see if the string starts with slash slash so if uh numbers that starts with slash then we need to do something with a custom delimiter and we can just say our delimiters now is up here and we could just do uh var numbers equals numbers dot split on black and slash n backslash n uh let's see this is the new delimiter actually new delimiter is numbers.split um dot first okay how do i make sure that's a character or do i care if it's a character i guess it could be a string so the var delimiter could be a string right string new delimiter and what if i make these strings does everything still work if i'm using strings otherwise i gotta cast to a character which i can do but my life gets easier if i just say they're all strings no you don't like string delimiters okay so back that out and instead i've gotta somehow get this delimiter into a care so there's a convert delimiters.add convert dot to care does it convert i don't use convert often enough here's the syntax for convert system dot convert of t or convert from how do i do convert convert dot to care from what anything right yeah so from the thing from new delimiter so a new delimiter so that's what i want i want that and i want that to be added to my list of delimiters but you don't like that why not care oh fine let's just make this a list so if our delimiters equals new list of care and you need to be a generic list now and now i can add to you delimiters.add with an extra paren and [Music] yeah i'll work up to those helpful stranger we're going to add delimiters dot to array and we're close we're not that close all right that didn't work and the reason is because i need to get rid of the the prefix on this thing so when i split this out it gives me two pieces so i really want to have numbers here and then when i get to numbers starts with even if i don't do numbers starts with i want to have something like string number string equals numbers dot split on the backslash n uh let's see and i want to skip the first one i think well no i can't skip the first one if it doesn't start with slash well i guess we just say number string equals numbers to start with so we're not i don't want to manipulate the input i'd rather manipulate my own local so number string equals numbers new delimiter equals okay so var uh split input equals numbers dot split on that on that cut that paste that get rid of that okay so split input dot first is the new delimiter and number string equals split input dot skip and there's probably an easy way to do this but we're going to skip the first one um that two i'm going to join it now i think i have to join it dot join that's not the join i want i think i need string.join all right so split input dot skip that gives me a list of string and then i can do string.join the care separator of this is probably not the easiest way to do this backslash n is again and then this thing right so there's my number string back to the way it was but minus the opening slash slash x slash n i think this still doesn't work though so what do we get in here one two three should be six i get the delimiter i add it um everything else is green uh someone suggests i just trim off the front slash slash but that doesn't get rid of the delimiter itself or the first new line that's splitting it so if i get if i trim numbers slash i get rid of those but i don't get rid of these so what i'm trying to do here is i say split input means the first item in that input when i split on slash n is this so that's what first is and that's that's why it's wrong so first includes that's what you're saying i should trim delimiters.add convert to care that's going to blow up because i need to trim it so this is new delimiter.first.trim that right that's what you're suggesting but that doesn't like that either because why string removes there why don't you like that you cannot convert from string to care split input is a string i get the first string and i trim it um i wonder if we just trim all instances of that with that no i still don't like it all right so i have this i really just want the last character but this could be multiple things let's use this way to just trim off those two characters why doesn't this work it moves all leading and trailing instances of a character all right so maybe that is working all right well let's do the unthinkable and debug into this let's see what it's doing so i'm going to debug that [Music] and wait forever this is why i don't debug very often because it's so slow and it's with a fast machine just thinking about it all right there we go so split input is null no new limiters and all split input is a string with two things slash semi123 perfect that's what i want and we step over this and now new delimiter is semicolon all right that's what i want number string is the whole big thing right that's what it should be step number string now is 172 semi three perfect delimiters.add do that all right so now we have delimiters which now has three things which is comma backslash and semicolon that's perfect so then we step over this and we get in parse s blows up why was the input string select int parse what was s s should have been numbers numbers was where's numbers where's my locals what did i lose the thing show me what it is input string was not in the correct format it was a one and a two and a three number string care versus string numbers is a string we saw it was number string it was the right one um all right let's do this again so break point on that this is the downside to link is that when this thing fails it's like well somewhere in there something broke um i don't understand why i can't see it still but that's fine let's just debug it again that test run took 1.8 minutes that's way too long all right so numbers uh there's the problem uh i'm using numbers that should be number string i got it bam okay get rid of that get rid of that when you do this type of thing and i always forget this and you switch from using your function input to using a local you need to make sure you change it everywhere else below so number string that could be number string that could be number string and that could be number string and i really wish there was like a c sharp feature that says from this point forward numbers is not usable uh don't don't use a past this point because i've decided to switch over to a thing i'm going to manipulate okay that all passed so yay and that was number different delimiter number four i guess we're done so step four done step four done all right um what's step five first line is optional calling add with negative we'll throw negative is not allowed in the list of all the negative numbers so example -1 comma 2 let's copy that there do this and take that now control x paste it in there negative 1 comma 2 doesn't actually do anything and this says throws given negative inputs all right so it needs to collect the negative inputs and then throw so we have a number string uh well let's finish writing a test first uh let's see action action equals calculator dot add numbers and then we say assert.throws throws uh exception type is our exception type just exception all right so type of exception do i even need that i think there's an overload for this where i don't need that or it's a throws of t maybe um do this r x equals that and insert that r equal this is going to be the exception message negative's not allowed colon minus one uh there expected massage all right now it says assert that throws there calculator add numbers or are you not happy you cannot convert from object to action that's because it needs to look like that there we go expected message equals ex dot message now our test compiles maybe or are you not happy because that's a string all right now our code compiles now i can go over here and write a thing that says um var negatives equals number string dot let's just get the numbers so var number list equals this all right result equals number list dot sum and that means everything still passes except for this new thing var negatives equals number list dot where number such that n less than zero um if negatives dot any then throw new exception um negatives not allowed and then negatives and now we just say string a negative string i feel like i'm getting close on time negative string equals negatives dot um dot select and dot two string all right that gives me a bunch of strings and i can do string dot join comma negatives blah blah blah that that select where are you not happy to string that there we go okay so now we've got the negatives we get over here the error says covered let's run this test see what we get succeeded what's your what's your result message is your equal failure there's a negative string literally negative string dollar sign thank you and everything's green again all right so now let's do another test and this will be negative one negative two and this will be native one comma negative two green again coolness so then we go over here and we did a step five done all right now step six says ignore over a thousand all right that's easy enough let's go back up to a simple test like this one and jump down here and say boom return sum given string blah blah blah uh ignoring values over 1000 like that here's our numbers there's our things one two three thousand excuse me three and what other things have we got one thousand one comma two let's try that one thousand one comma two should just return to um those fail so now we've got a number list um here's our sum we really want to sum n such that n is less than a thousand what about edge conditions uh numbers greater than thousand so less than or equal to uh why are you not happy i cannot convert bull to long some some takes an expression right i guess i'll just do a dot where and then get rid of that i thought you could do an expression on there why are you not doing it okay that works all right so that works and we might as well try a thousand as our edge case right and that should be a thousand two good all right so that was step six and we're done all right okay i get it google stop it stop it um all right so let me check that in uh step six commit all and that was not bad okay so i haven't pushed all this up yet so let's do that and now i'm just going to go show you what the cool tool looks like with git history so bring this over here and refresh this and then we can jump into the source and there's our thing from today and we did string calculator and here's what it all looks like and we can say openness and get history and so this is github.gethistory.xyz and then slash the path that you're using in github all right so you can literally just add you've replaced.com on github with dot get history to xyz there's also a chrome extension which is what i'm using um but if you jump all the way back to the beginning and if you're good enough to or you know if you have the presence of mind uh to do a lot of commits when you're doing one of these it lets you kind of step through this and say okay well first we just returned zero and then we checked to see if it was null or empty and otherwise we return one and then we parse the the numbers uh string into an integer and return that and then we split it on a comma and parsed it and gave back the sum and then we pulled out the delimiters and selected and then parsed and give the sum and then etc etc right so you can kind of see how your code evolves this way now there's still probably some final uh refactoring that we could do in here and and certainly you know if i spend a little more time looking at this i wasn't trying to get through everything i would look for that but this is reasonably clean um you know eventually i wanted to pull out numbers as their own separate thing i didn't need that initially but but i did once i needed to start dealing with negatives and uh and be able to display them it'd be easy to just like throw an exception if there happened to be any um but the the fact that they wanted to have them around meant that i needed to be able to take that collection of negatives and actually work with it and that's why i wanted to uh to pull those out separately but yeah that's pretty much it so if i go back to the kata which i've lost uh so here with the timer we got through step six but there are still three steps left right so i could have gotten through three more and if i had this um if i'd done this more recently and maybe i would have i would have gotten through it but you can see it's it's meant to be a challenge to to get through the whole thing um yeah so that's pretty much all i wanted to do today uh if you want to see the code for the kata is is here the code that i just did is over here um i'll give you this right here we'll throw that in the chat so that people see it um and then you won't necessarily have if you open this up yourself you won't necessarily have this button open and get history that's the chrome extension i've got but again if you just take this dot com and change it to get history.xyz on a particular file it should open it up for you like this alright then you can see it last but not least if you want to do this type of exercise with a bunch of folks that are trying to get better check out devbetter it's a group coaching program i run with a bunch of great people um we're we're always happy to see new faces it's not free um part of the reason why we charge for it is because i only have so many hours in a day to mentor people and because we want people that are there to be committed to actually wanting to improve themselves so that's my brief advertisement here again for for this stream uh with that it's now midnight my time and i should probably uh get to bed so uh thanks you all for hanging out and folks on youtube uh thanks for watching feel free to subscribe if you want to see more streams and things from me bye y'all
Info
Channel: Ardalis
Views: 1,289
Rating: undefined out of 5
Keywords: ardalis, asp.net, asp.net core, c#, ddd, design patterns, dotnet, games, kata, nuget, result, specification, stream, string calculator, tdd, twitch, visual studio
Id: H96nnZuQO00
Channel Id: undefined
Length: 45min 0sec (2700 seconds)
Published: Tue May 25 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.