Jeremy Bytes - TDD: Don't Turn Off Your Brain

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
this is Jeremy Clarke of Jeremy bytes calm and I want to continue talking about test-driven development now last time we took a look at the basics of TDD and that gets us started in the process but I want to take this a little bit further when I do test-driven development I'm usually not taking baby steps as I go and I don't let my design come entirely out of the tests when we're doing test-driven development we still have to think about the code that we're writing and how it fits in with the overall scheme of our application test-driven development uses a red green refactor approach first we start at the red face by writing a failing test then we write just enough code to get our tests to pass and that puts us into the green state after that we can refactor our code to make sure that it's clean and maintainable now where I start to bend things a little bit is when we get to the writing just enough code to pass because generally I'm thinking ahead about the design that I want for my application so I'm not always writing the minimum amount of code most of the time I'm writing the minimum amount of code that goes along the path that I'm looking for now today we're going to be looking at something known as Conway's Game of Life this is a cellular autonomous simulation and it's something I was intrigued with when I first started getting into computers many years ago there's a set of rules which we'll look at in just a moment that determines whether each cell on the grid survives to the next generation so each time we move to a new generation we'll see that the state of our cells changes and if we do this enough times we'll see some interesting patterns form and that's what makes it really intriguing to watch Conway's Game of Life in action so let's take a look at the rules for Conway's Game of Life for this we look at a cell and take a look at its eight nearest neighbors this will determine whether it survives to the next generation or not the first rule is that if we have a live cell with fewer than two neighbors it dies due to loneliness if a live cell has two or three neighbors it survives to the next generation if a live cell has more than three live neighbours it dies due to overcrowding and finally if we have a dead cell that has exactly three live neighbours it springs to life so let's take a look at some examples if a cell has fewer than two live neighbours it dies due to loneliness if it has two or three live neighbours it stays happy and stays alive if it has more than three live neighbours it dies due to overcrowding and if a dead cell has exactly three live neighbours it springs to life and with these four simple rules we can get the simulation that we see here and again we can see some really interesting patterns forming and we also see some stable states that start to develop so let's head over to our code and use TDD to generate a library that implements these rules so here we are in Visual Studio and I've already stepped out some projects for us the Conway dot library project is what's going to hold our code the Conway library dot test project will hold our unit tests now I've already set up these projects and added new get references in order to use and unit as our testing framework if you want to walk through on how to set that up you can take a look at the previous video TDD basics we have our life rules class and this is where we'll put our business rules and then we have life rules tests which is going to hold the unit tests now since we're starting with the tests first I've actually put the business rules inside the test class itself this way we can use these to generate the test methods that we want so let's go ahead and start with this first test so we'll go ahead and just mark this as a test and we'll say public void and then we have to start thinking about what we want to do well let's go ahead and take this first rule any live cell with fewer than two live neighbours dies so let's say live cell fewer than two live neighbours dies and this is where I have to start thinking about the design that I want for these particular rules I could create a cell object that knows about its state knows about its neighbors and then can calculate whether it's going to live or die and that's something we might want to do in classic oo programming where we want each object to have both state and behavior but what I'm thinking about here is how we're going to process these rules let's flip back over to our example now each time I go to a new generation we have to recalculate the rules for every single cell on this grid that means depending on the size of our grid we'll have to calculate it hundreds or maybe even thousands of times in addition to that I'm going to be running through quite a few generations before this actually hit it's stable State so these calculations are going to be happening a lot now because of that I'm probably going to want to run these calculations in parallel and in order to do that I'll want to use more of a functional style program than an oo style program with a functional style method we pass in discrete parameters and we get a discrete answer out we don't mutate and this way we can run all of these calculations in parallel so that we can run things more efficiently so even though we are doing test-driven development and our design comes out of our tests we don't turn off our brains here we do still have to think about the direction that we're going in so let's head back to our tests and see what we can do to put this in place now to start with I do need to consider the current state of our cell in this case we want it to be alive when we start out with now I could use a boolean value here and have true for live and false for dead but that really doesn't feel quite right what makes a lot more sense to me is to actually have an enum that will hold this state so I'm actually going to write a little bit of code so I'm going to create a new public enum called cell state and we'll give it two values alive and dead and again a TDD purist might be a little upset at me because I'm writing code before I have failing tests for it but I also think about my knowledge of the situation I'm an experienced developer I understand this problem space fairly well so there really is no reason for me to not move forward quickly when I know a good direction to head in so instead of having true here we'll use cell state and I'll need to bring in the using statement for this so I'll just do control dot to bring in using Conway dot library and then we can say dot alive for our initial value now I also need another variable for the number of live neighbors in this case we'll just go ahead and set it to zero since that is fewer than two and now that we have our initial setup let's go ahead and perform our action well what do I want to do well ultimately I want to get a cell state back and this will be the new state so that we can tell whether it's alive or dead and again for this what we really want is to have a functional style method that takes discrete inputs that will return this value so we'll use our life rules class and what I'm going to do is treat this like I have a static method on this class and I'll go ahead and call this get new state and then for those discrete parameters we'll pass in the current state and the number of live neighbors that we have and then finally we need our assertion what do we expect to have happen well in this case we're going to expect that our new state is dead because we expect that our cell will die from loneliness so let's go ahead and put in cell state dead as our expected value and new state the value that's coming back from our method as the actual value now if we build our code at this point we see we will get a compiler failure and as we learned last time a compiler failure is the same as having a failing test so we are in the red state right now and that's because I need to generate this get new state method so for this I'm just going to go control dot and then we'll see we have an option to generate a method and when I hit enter here and flip over to my life rules class will see that this created a new public static method called get new state we can see that the parameters include the current state and the number of live neighbors and it returns a new cell state as the output now I know throwing a not implemented exception is not what we want here instead what I'll do is put in some kind of reasonable default so let's just say return current state so by default this method will just spit out whatever state was passed in as a parameter now if we build and run our test at this point we will have a successful build but what we'll see is that our test fails and if we look at the error messages for this we'll see that the expected state is dead but the actual was alive so let's write enough code to get this rule to pass so we can say if current state equals cell state alive and live neighbors is less than two then while we turn cell state dead and when we go ahead and rebuild and run our tests now we'll see that our test does pass now we're only testing one value here and that is zero for the number of live neighbors what I'd rather do is test bold zero and one so I'm going to parameterize this test like we learned how to do last time so an n unit we do have a values attribute that we can use and we'll go ahead and pass in zero and one for our values of less than 2 and our parameter will be called live neighbors now since we have this parameter we can get rid of the local variable and when we rebuild and run our tests we'll see we now have two tests one that's testing for zero and one that's testing for one so we have a pretty good start here let's go ahead and move on to our second rule and our second rule says any live cell with two or three live neighbours lives so public void live cell two or three live neighbours lives and since this test looks a lot like the previous one let's go ahead and just copy the parameters and method body for that so for our parametrized values we'll have the values of two and three and then the only other difference is that we expect that our cell will be alive after we get the new state so let's go ahead and rebuild and rerun our tests and we'll see we have two new tests that come up and both of them pass seems like we skipped the red portion here and actually we kind of did and that's because we put that default value in our method since our cell state coming in is alive and our expected state is alive it just falls through to the default so let's go ahead and move on to our next test and for that any live cell with more than three live neighbours dies due to overcrowding so let's go ahead and create a new test for that and we'll say public void live cell more than three live neighbours dies and again we'll go ahead and just kind of copy most of the test that we have above and change the things that make this easier to work with so our values are more than three so let's go ahead and just start out with for for now and then our expected state is that this should be dead so let's go ahead and change the state and when we rebuild and rerun our tests we'll see that we do have a failing test at this point and if we look at the results we see that it's expected to be dead but the actual answer is alive so let's go back to our code and add a conditional for this so if current state is alive and live neighbors is greater than three Returns cell state debt and when we rebuild and rerun our tests will see that our test does come up green but now we want to add a few more values to the now I could say four or five six seven but I really don't want to pass all of those in in addition to the values attribute and unit also has a range attribute so I can say range 4 comma 8 and when we rebuild and rerun our tests what we'll see is now we have four new tests for a total of five for this one method so it tests four four five six seven and eight by basing our parameter on this range so we've implemented three of our four rules so far let's look at the last rule any dead cell with exactly three live neighbours becomes a live cell so let's go ahead and write a test for this so in this case public void or starting with a dead cell and if it has exactly three live neighbours it lives now since we only have one value here I don't really need to parameterize this live neighbors value so we'll just set it to three and let's go ahead and just copy the code from above and change our values so our initial State will be dead rather than alive and then our expected state will be alive instead of dead now when we rebuild and rerun our tests we'll see we do have a failure and that's because we don't have code that checks for this condition so let's go ahead and add this so if current state equals cell state dead and live neighbors equals three Returns cell state alive and when we rebuild and rerun our tests we'll see that our new test now comes up green now even though we've implemented rules for all of our tests I actually want to add a couple more just for sanity check purposes for our live cell we are actually checking values between zero and eight which are the valid values for live neighbors in this case for our dead cell we only have one test testing for three so I actually want to test the other cases as well so let's create a test for fewer than three live neighbours so I'll say public void dead cell fewer than three live neighbours stays dead now we're going to start out by parameterizing this so I'll set up a range from zero to two and this will be an integer for live neighbors and then again we'll go ahead and grab the code from our tests above paste it in here our current state is dead but we expect that our new state is also dead so we'll go ahead and change our expected value and then just rebuild and rerun our tests now in this case we'll see that we do get three new tests and they are all coming up green and that's because we're not changing the state so they're just falling through to that default value so let's just write one more test for more than three live neighbours so public void dead sell more than three live neighbours stays dead and we want this to be a range of values as well so we'll go ahead and copy our parameters and method body from above well change our range so it's from four to eight and then the body of the method will be the same our current state is dead and our expected state is also dead so now when I rebuild and rerun the tests we'll see we have a total of eighteen tests come up and they are all green and that's good it means we're testing the number of live neighbors from zero to eight for both a live cell and for a dead cell so we're covering all of our conditions here now before we leave this code let's go ahead and do a little bit of refactoring here I like to use switch statements especially when I have enums coming in so if we switch on the current state then we can create sections for both cell state alive and cell state dead so let's go ahead and just move this code up into the switch statement so our first condition is alive and live neighbors less than two so we'll go ahead and say if live neighbors is less than two returns cell state dead and that takes care of this code here now if our live neighbors is greater than three our result is also dead so I can go ahead and put in an or conditional here say if it's greater than three it's also dead and in the case of our dead cell if we have exactly three live neighbours then we're going to return cell state not alive and so we can get rid of this code as well I'll go ahead and get rid of the default since we really don't need it in this case and now we can see we have our rules expressed in a little bit of a different way and if we rebuild and rerun our tests we'll see that all eighteen tests still pass and we have a very functional style method in this case it's taking discrete values the current state and the number of live neighbors and returning a new cell state we're not mutating any existing values and what that means is that we can use this method very easily in a paralyzed structure so we can run this method multiple times against different cells all concurrently and we won't have to worry about any state mutations or locking threads and if you want to see the rest of this code that actually hooks this up to a grid so we can see this in action just head over to the links in the video notes and you'll see how we can hook this up to a console application so today we've seen how we need to make sure we don't turn off our brain when we're doing test-driven development even though we want our code to come out of the tests we still have to think about the direction that we're headed in in this case when we were talking about Conway's Game of Life it was really important to have a functional style method so that we can easily parallelizing our implementation now our code isn't perfect at this point in time it actually has some bugs and next time we'll look at how we can fix those bugs by first writing failing tests to capture the bugs and then writing just enough code to fix the issue at the same time we'll look at how we can test for exceptions in our code it's something that's not real difficult but our unit tests frameworks can help us out with this so until then be sure to visit www.carmensognonvi.com/newsletter
Info
Channel: Jeremy Clark
Views: 18,888
Rating: undefined out of 5
Keywords: Test-Driven Development, TDD, C#, Conway's Game of Life
Id: 6OQyv6CFbPo
Channel Id: undefined
Length: 18min 23sec (1103 seconds)
Published: Sun Mar 06 2016
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.