 Let's start now. OK, today's session is our brief introduction of this technique called the so-called test-driven development, or TDD, in short. So I will give you a brief introduction, and then we will do some live coding or exercise quickly together. And after I give you some simple exercise, then we can do some cutter. And this session will take about three hours. So let's start. OK, just some brief history. Since many of you probably know a bit of TDD, so who knows that who is the guy who discovered this technique called TDD? Does anyone know the name of the guy who coined this term? Martin, not Martin Fuller. Unfortunately, not Martin. OK, his name is called Kent Beck. So this is the gentleman. And he wrote a book called the test-driven developments by example. And he made this technique famous. And so what is this technique is about? So I will go a bit faster, since we started late today. But in short, TDD is basically our technique for you to write test cases during the development. And what makes it unique is you write your test cases first before you start writing your production code. So as you can see in this chart, there are three steps you really will do when you follow the TDD approach. The first step is to start writing a test case. That, of course, without a real business logic or a business de-implementation that make this test case pass, the firstly it will fail. And then the second step, you are going to write some code to make this test case pass. And in the third step, once you make the test case pass, if you use any unit test framework, the color of your test cases would become from red, become green. And after that, you can do some refactoring in your code to make your code clean, easy to read, easy to maintain, et cetera. And once you're done with the third step, then you can go back to your first step. Write another test case, make it pass. And then refactor your code and test cases. Looks pretty simple, but actually later you will see that actually there are many areas you need to pay attention to in order to be more proficient in this technique. So for example, many people, it's very hard for many people to get started by writing test case. Because by writing test case first, you will see some of errors reported by our compilers, et cetera, and that makes you nervous. And then also, in the second step, later you'll see that many people, there's a pitfall that sometimes you tend to write more code than necessary to make your code pass. And for example, some people, well, developers writing the code, they tend to think of different conditions, like what if A happens, what if B happens, what if error condition happens, your mind will just keep thinking about those things, while many of those things are not really related to your current task to make your test case pass. So we need to try to avoid those pitfalls. Then in the third step, many people, again, once you write your test case and you make it pass, sometimes we just forget. We just move on to the next scenario on the next test case without looking into our current implementation. Are there any room for improvements, et cetera. So sometimes you also easily forget about the third step. So this is very simple, but it definitely takes quite some practice before you are familiar or before you cannot do this every day. OK, so just to quickly show you some of the benefits, why this technique is useful. So if you follow TDD, you can write clean code that works. So not only clean code, but clean code that works. There are a few reasons. One reason is if you follow this TDD approach during your development, typically you can get faster feedback about whether you have done something wrong, whether your changes are correct, whether you introduced bugs, et cetera. Typically you need those kind of feedbacks whenever you make changes into your code. And the TDD is an approach that can give you faster feedback. So I show you two graphs here. The graph on the left basically shows you the different techniques or different tools that give you feedback during the development cycle. So you probably notice some of the terms like prior programming, unit test, standard meeting, acceptance test, iteration plan. So there are different ways for you to get feedback from your development lifecycle. And you can see that some of the techniques give you faster feedback for unit test. Typically if you write unit test to test whether you did something wrong or whether your changes are OK, you can easily get feedback in a few minutes. But let's say if you only release everything into production and then test in production, so-called test in production, and then once you discover something happens, goes wrong in production, and you would like to fix that bug or whatever, it usually takes longer time. If not days, could be weeks, et cetera, depends. So you can easily see that if you write unit test, et cetera, those feedbacks are faster. And why faster feedback is better? Because if you look at the chart on the right, this gives you kind of a graph to show you the cost involved in order to fix some issue discovered by those different feedback loops. So if you discover some issue that using the TDD technique, you can see that the cost is relatively low, because you can quickly fix a problem even before you release it into your QA environment or your production environment. But then if you discover these issues later using some other feedback loops, such as during your functional testing or acceptance testing, you usually tell you that something is wrong. And if you want to fix something at that time, the cost would be much higher. So just in short, if you follow TDD, you get faster feedback. And you can discover bugs or issues much quicker. And then you will introduce less cost in maintaining your code base. Another benefit, which is if you follow this TDD approach over the period, you will accumulate a set of test cases. Those test cases will become a safety net for you whenever you make new features into your system or whenever you make changes. If your changes happens to introduce some bugs, then the test cases will fail. And you will get notified quickly. And you can basically help you to catch the regression errors easily. And on the third benefit, if you follow TDD approach, it helps you to focus on one task at a time. So for example, just now we show that there are three steps during a TDD approach. And you should focus on one thing at a time. You should either be focusing on writing a failing test or you should be trying to make the test case pass or you should be doing some refactoring. So you should focus on one of the three things at a time. And if you follow this approach, your mind will be very focused on that task. Once you make that task pass, move on to the next one. And if you get into this kind of a rhythm, you can notice that your performance will be improved as well, because you are focused. And this also later I will show you if you follow these techniques, that usually you will be moving in baby steps. And the baby steps basically means that your task is easier, simpler, it's faster to implement, and easier to make it right. And then another benefit is if you follow this approach by writing test cases first, before you write your production code, one additional benefit it forces you to think from the end user's perspective. Let's see if you are writing an API. And then if you write the test case for that API first, basically that forces yourself to think, oh, how should I develop this API? What are the name? What are the parameters? When you think of those things, actually you are thinking from the end user's angle. And that basically helps you to write some APIs that are easier to use, and at least easier to test. So that makes your code easier to maintain in the long run. And another benefit is you can produce less waste in your developments. So just imagine that if you put in two ways to view this. One way is if you always write your test case first and you only write enough production code to make the test case pass, you're pretty sure that whatever you write is needed because you start with a real useful test case and you know that whatever code you write are useful because they are used or tested by the test case. On the other way, like if you start writing your production code without any test case, you might think about different scenarios and make your code very complicated. But then it turns out that in the end, some of the scenarios have happened. Some of the code that you wrote are never used. So that can happen because you never drive them from the testing approach. Maybe let's start our first TDD exercise. So do you all have your environment set up already? OK, I will do it together with you. So I probably, because many of you are new to TDD, so I will probably just write the code first. You can observe what I'm doing. But if you're already familiar with TDD, you can start writing code at the same time while I'm writing it. OK, so the first exercise we are going to do today is to implement a number sequence generation, sorry, number sequence generator using the TDD approach. So I wouldn't tell you what this number sequence is, but I will give you the number one by one. And let's write test cases each time when we see a new number and then we will slowly, one by one, discovering the pattern in this number sequence. So let's see. OK, maybe. OK, let's start up your project first. OK, just a moment. So let's set up the project together. For me, I'm going to use Java today, and I'm going to use IntelliJ as the IDE. I chose it. I know that many of you know JavaScript and are probably not familiar with Java, but it's OK the language syntax isn't that difficult because all these problems are simple. You can do the similar thing using your preferred language as well. I'm going to show it using this IDE because also in later sessions, I'm going to show you some of the refactoring techniques. And Java and in combination with this IDE makes refactoring very easy. So this is the reason why I chose it today. OK, let's start all create a new project. Using whatever language that you prefer, you can start creating a new product. I'm going to create a new project using Java here. So Coding Gym is my group ID. The project name, we call it Magic Number Sequence. And oh, it's too small, right? OK, once I start the IDE, I will make the font bigger. OK, so I just finished setting up my project. I'm ready to start writing my code. So are you guys ready? Just make an empty project. Don't write the code first. I will show you the first number. And once we see the first number, we will start writing the first test case to develop the logic for this number generator. OK, let's see, what is the first number? Where is my? OK, here it is. OK, so the first number in this sequence is 0. So now I'm going to start writing my code. But as you will see, I will start writing from the test case. And then after I finish the test case, I will move on to make the test case pass. And then I will do some refactoring along the way. OK, let's start. OK, where do I start my test case? In Java, I need to put the test case under this test Java folder. Let's create our new test case. Let's call it magic number, maybe magic sequence. OK, magic sequence test. OK, I just call this magic sequence test. And I will create my first test case. And my first test case, I would call it let me increase the font. Come on. Is it better? OK, maybe just use this. Change your light. OK, sure. And let me change to a light sim. OK, how do I change the sim? It should be Contruder, Coastal, Scim, or color sim. Yes. Yes, I'm going to change it. It's so ugly. You kick it after you restart the ID. Side glass. First sequence is zero. Yeah, the first sequence is zero. I'm about to write my first test case, but then stop to buy all these issues. Come on, come on, where's my IntelliJ? Oh, it's here. Can I ask this presentation mode? Oh, right. You can try that. OK, so it's View. Presentation. OK, let's see. Is it better? OK, maybe I can still make this font bigger. My magic mouse doesn't work. OK, let's increase font size. I'll do it again. This seems to be the biggest. OK. OK, let's continue with my first test case. I would name my first test case as test. First number is zero. OK, so then, usually for your test case, you need to write some kind of assertion to assert that your code behaves in a certain way. Using whatever language testing framework that you chose, you can write your assertions. For Java, I'm going to write it as assert equals. And assert equals is an API provided by the JUnit library. So I just need to import this function so I can use it. So I will assert that I will have a new magic sequence. I have a new magic sequence. And from this magic sequence, if I try to get the first number, I'm trying to see that I'm asserting that for this magic new sequence, if I get the first number, I should get zero. So this is my first test case. And then, once I have this test case, you can notice that there are already some errors. So I'm going to make those compilation errors disappear first before I implement the logic. So as I will show you, I'm going to use some of the shortcuts in this IDE, which makes the coding easier. So I'm going to introduce the class called Magic Sequence here, where I'm going to put it under this main Java package. This is just some Java convention. Ignore this if you've found this piece unknown to you. Otherwise, now I have this magic sequence. And then next step is it shows that I don't have this get function or get method. So I'm going to implement this one as well in this class. So this function will take in our integer as input. For this input, I will just call this one as index. And then this is the empty function. So I have a test case. And there seems to be no compilation error. Actually, there is. This function needs to return something. Let's see if I'm going to return minus 1 now randomly. And let's see. Do I have the test case right now? If I run the test case, OK. Where's my test case? Come on. Yeah. Come on. But then in the presentation mode, it does not display that. Unfortunately, I need to end. Shift T as I've just told you. Shift T. OK. Does it work in this way? OK. You can see some ugly window below, which shows that my first test case is failing. And in my first test case, I'm asserting that my first magic number should be 0. But then my current implementation returns me minus 1. So OK. So now I'm moving from the. Remember, for TDD, there are three steps. First step, implement your test case. Second step, make your test case pass. So I'm already done with the first step. My test case is done. And it's already asserting my first number should be 0. And then it's time for me now to move on to the implementation to make this test case pass. And remember, the second step is to make the minimum changes that you can, or simplest changes you can, to make your test case pass. And in this case, I don't know anything extra. I only know that the first number should be 0. So the simplest thing I can do here is just to return 0. And this may look a bit silly right now. But it's OK because this is the only test case I have. And this is the only thing I need to do to make it pass. I don't need to do anything extra. So let me just run the test case. I'm running the test case again. And now it should be passing. Maybe I should just move my window to, OK, move this window to right. Pin dog mode, floating mode. Maybe window mode. Oh, it's not better. OK, whatever. Maybe just move to right. OK. Hmm. OK. I'm not sure whether this is better. So now we have this first test case. And you can see that now it's already passing because I just implemented the function to make it pass. And now let's go down to the second number. Can I just get a sense of how everybody got the first test case written, bailed, and then passing? Who's not got it so far? OK, so maybe let's just get everybody to get the test case. Sure. OK. Michael, maybe let's pause the recording first. Let's give people some time. OK. Yeah, usually it's a bit hard to get start. But once you're done with your first test case and make it pass, then the second one, third one, will be easier. Let's see the second one. The second number in this sequence is one. So again, I'm going to change my code to make this pass. But firstly, I need to write a test case first. OK. Let's start by writing the second test case. OK. The second test case would say, oops. It say that the second number, I'm just changing it. Second number is one. OK. And so I just need to change my test case. The second number argument should be two. And the second number should be one. OK. It's all done with the test case. Quickly copy paste. Oh, copy paste is a bad habit. But for this case, it's OK. Whatever. Write the second test case. And if you run a second test case before you make any change in your code, then it should be filling. Like what I'm running here. Oops. So I need to do it. So I just need to run the whole thing. OK. I got my second test filling. It's filling because I haven't modified my code. And my code is still, even for the second element, my code is still returning zero. But then the expectation is second element should be one. Are you guys all at this step now? At the same pace? OK, cool. So now let's think about, we are moving from, for TDD, we are done with the first step. The test case is filling now. And now let's move on to the main code to make the test case pass. So let's think about it. How can we make the both test case pass? What are the minimum changes we can make here? So it seems that if the argument is one, I'm going to return zero. And if the argument is two, I'm going to return one. So based on this two test case here, I think the minimum thing I can do here is just to change this return value from zero to index minus one. Do you agree with me? This is probably, there could be other implementations. But for now, to me, just given these two test cases, in order to make them pass, this seems to be our decent implementation. Just taking the index, minor it by one. And then let's see. It should pass now. Yeah, both test cases are passing. So can you also quickly implement your test case to make the pass as well? Do you all make it pass? We probably need to constantly pause and resume, pause and resume. OK, now we have the second test case passed. But before I move on to the third test case, do I miss something? Do I miss one step? Yes. Yeah, that's easily forgotten. So for now, before I move on to the third test case, I need to have a look at my test cases and my code to see are there anything that I possibly need to refactor. So my code is pretty simple. And I don't think I need to change anything. And my test case, there seems to be a bit of a duplication here. But I'm not sure at this moment. So for now, I've just decided to leave it as is. And OK, now I'm done with my thinking on the refactoring part. In a typical development, this usually happens in a few seconds. You just quickly check your code. Is it good? Yes, then move on. And then my first test case, let's see, what is the first one? What is the third one? OK, the next number is also one. OK, the next number is one. And then let's start by implementing our test case to make that assertion. OK, the third number is one as well. So I just need to change my argument from two to three. And this is basically that my third number should be one. And if I run this test case again, you can see that my test case is filling right now. Because according to my implementation, if I pass in the number three, this value returned here would be two, which is not one. But for this test case, I'm expecting the third element to be one as well. So my test case is filling. Do you all have this filling test case now? Do you have a filling test case now? Maybe let's pass for a while. Pass. But at this stage, there will be different possible implementations. You can choose your own, and I will just choose mine. In this case, I'm just saying that, OK, my rule used to work here. But now with the third arguments, with a new argument, it doesn't work anymore. So one possible way for me to make this change is I would just say if index is smaller than three, I will just use this old logic. Otherwise, I will just return whatever result I need. In this case, they are expecting a one as a result. I will just return one as a result. So, OK, after this, I'm going to run the test case again. And yeah, it all passed. OK, do you all make your test case pass now? I will give you probably five minutes. For JavaScript test library, they allow you to write some description for your test case using a describe sentence or whatever. You really, in that sentence, you need to put some sentence words to describe the purpose of that test case. But in just like what I'm doing here, for this test case I'm writing, so I usually choose to make the test case longer so that it's more descriptive to basically express what I'm testing now. And now we have the third test case and it's passing. Again, before we move forward, we need to just pause, quickly check. Are there anything to refactor? Again, the code looks simple. And test case, again, there are some duplications. But for this one, there's a choice here. For you guys, if you're familiar with something called parameterized test, basically there are some utilities in every language. When you write test cases, you can write some test case that's taking some dynamic arguments from some kind of source. And if you use that, you can simplify your test cases. You can just write one test case and that test case can test all the scenarios like taking the first element, second element, third element, et cetera. But for now, I'm still happy with this, so I will not do further changes. And let me start with the fourth test case. Okay, I'm going to start the fourth one. The fourth test case is the next number is two. Let's write the test case to make it filling and before we make it pass. The fourth number is two. Okay, I wrote this test case, the first step, and if I run it, it should be filling. Okay, it filled and then the next step is to make your code changes to make it pass. So now I'm moving a bit faster because I see that many of you already got this idea and the sequence, so let's do it a bit faster. So now I just need to make this test case pass. So how do I make it pass? If I pass a number of four, I'm expecting two, retent. So what is the pattern here? Now I'm going to, given enough test cases, I'm trying to make some observation in the pattern and also try to implement the pattern in my code. So it seems that starting from this third number onwards, I can observe the pattern. Like the third number, one, is the sum of the previous two numbers like zero and one. And four, the fourth element, which is two, is also a sum of the previous two elements. So in this case, I found this pattern. So I'm going to implement this pattern in my code. So what I'm going to change here is I will change my implementation to return the result of the first two elements. I'm going to use a feature called recursion in programming for those who are not familiar with this. Basically, recursion is a function that calls itself and by passing in some simplified or different argument that simplify the problem. In this case, I'm saying that I'm going to call the same get function again, but with the different index. Those two different index basically represent the two previous numbers. One is index minus one, which is the previous one, and the index minus two is the previous two. So basically this line is saying that if I want to get a number at this index, I just need to get the number at the previous one and another number at the one before the previous one, and then I take a sum of those two. I write my code in this way because I notice that there is a pattern here, like this number is a sum of the previous two and this number two is also a sum of the previous two. So I'm going to write my logic in this way and to make this code pass. If I just run my code, it should be passing now. So now I'm done with my second step. So I wrote the filling test case and make the test case pass. And my third step, do I need to do some refactoring? At this stage, now it seems that I found a problem. Actually, I should notice this problem just now. I have some duplicated code here. I'm trying to reconstruct this new magic sequence one by one, and it seems a duplication because I just need one instance of this class. For those who are writing code in Java, you probably follow my code and you also have this issue. But if you are writing a JavaScript, et cetera, and you just have a function and you may not have this duplication, but then that's one. For me, I noticed that this duplication in my test case, so I'm not happy about it. And for now, before I move on to my new test case, I'm going to do some quick refactoring to extract this common thing into a verbal so that I can reuse it. Basically, it's a reminder that you need to refactor your test case as well to make them easier to read and maintain. So for me, I'm going to do a refactoring in Java. If you use this IDE, it provides some of the easy tools for you to do the refactoring. In this case, I'm going to extract this new magic sequence as a verbal in the field. And I want to set up this verbal in the setup phase of the test suite. So as you can see that this IDE generated this chunk for me. So basically, before each test case, they're going to create a verbal called magic sequence. And I can use this verbal in all my test cases. So in this case, I'm going to just quickly reuse this verbal. I'm going to reuse this verbal by just doing a replacement. Okay, do a replace, replace, replace, that's it. So now I removed some of the duplications and the test case looks a bit better. And let me just run it. The test case still pass. So basically, for those who are not familiar with the term refactoring, basically it's a process to improve your code, change your implementation or making a code either to read, either to maintain, but it does not change the behavior of the code. And we know that in this case, I do not change the behavior of the code because my test case still pass. And I didn't change my logic itself. So the behavior is maintained. And I just made some refactoring to make the test case easier. Sorry, cleaner. Then one more thing I noticed that now I have this four test case here. And okay, I seem that I found a pattern, but I'm not sure whether this pattern is correct or not. So let's see the next number in this sequence. So the next number in this sequence is three. Again, so let's start by writing the test case to express this condition. I will write another test case here. And in this case, I will see the fifth number is three. Okay, I'm adding a new test case and let me run it. Okay, this test case does not fail. Basically, that seems that I found the right pattern in this sequence. And without making new changes to the code, my new test cases automatically succeed already. Which is kind of the clue that I probably already got the pattern in this sequence. Let me just check. Let's see. Okay, in the end, the fact is this sequence is actually called the Fibonacci number sequence. And for those who are familiar with mathematics, so this sequence is basically defined in this way. So it starts from either zero or one, and then the next number in the sequence is always the sum of the previous two. So this is the definition of this number sequence. And I just discovered this pattern while I'm writing this code here. I get a number one by one, and I slowly found out the pattern in this sequence, and I implemented the pattern in my code. Okay, now it seems that it's working. But again, remember the last step of the implementation is always do the refactoring, either in your code or in your test case. So in this case, I found that my test cases seems to be duplicating each other, because here I have a rule saying that from the third number onwards, it should always be a sum of the previous two numbers. So which means all these three test cases here, the test case for the third number, for the fourth number, and the fifth number, actually they are all testing the same behavior or testing the same rule in my code. So in this sense, they are kind of duplicating each other. So I would, in this case, just change the name of this test case to make it more descriptive according to the real rule of behavior it tests. So in this case, I will change it to, I will say third number onwards is a sum of two previous numbers, or previous two numbers. So I'm changing the description of this test case to make, to basically reflect what behavior it is testing in my code. And for those who are writing your program using JavaScript, you need to change the description, your describe block, or eat block, whatever, to make sure that the description of your test case match exactly what you're testing. And in this case, I could just copy this set equals here and delete all the other few test cases. In this case, after I do that, I should run all this thing again just to make sure that I don't break anything. Just run them again. So my test cases are still passing. Okay, maybe I should pause for a while and just make sure you guys also finish your changes. Make sure that you should also in the end have three test cases. One test case to check that the first number is zero. Second test case to check the second number is one. And then you have another test case to basically asserting that from the third number onwards, the value is always a sum of the previous two numbers. In the end, you should have these three test cases by after you do some refactoring of your test cases. And then they should also pass as well. Do you all have this now? I'll give you a few minutes. Make sure that you all arrive at this step. So now we are done with this test case. But usually, at this stage, you probably, again, you can now look at your code to see that are there anything that you want to test or you want to do some assertion. So one thing I notice is for this function, it seems that the original requirements is that it's starting from zero. Sorry. Okay, sorry. Just let's take that back. So what I'm trying to say is for this parameter index, it probably doesn't make sense to pass me a zero or negative number because I'm trying to get the first number in the sequence, second number, et cetera. So this parameter, by right, it should always be positive. So it doesn't make sense to have a zero or negative number. So in that case, typically as a good programmer, you want to write your code to check those conditions and probably throw some errors or exceptions when you are receiving that kind of argument. Again, so let's quickly do it for now. But in order to do it, do not write changes in your code right away. You should start with a test case. So for Java, typically, I would write in this way. I would write a new test case. And this test case would just say this one should test. Rather, I can see that exception is thrown when index is zero or negative. I can just have a test case for zero first. And basically, if I'm going to call this a magic sequence dot get zero, I'm expecting some exception. And depending on the library you're using, if you're using JavaScript, you probably need to do some assertion to expect that some error is thrown from the function. And every test library have different ways of handling that. So you probably need to check the documentation of the test library you use to see how you can assert the error is thrown from a function. For Java, typically using JUnit4, you can just do this way. You see, for this test case, I'm expecting an illegal argument exception that is thrown from this particular function. And this is how I write a test case to assert that I got exception from this. And if I run it, you can see that this test case should fail because I haven't handled this particular scenario in my code yet. So I could just see that if index is zero, then I should throw a new illegal argument exception and keep some description saying that the index must be positive. And after I do this, so I'm already moving on to the second phase of the TDD. I made the test case fail and now I'm doing some code changes, the minimum changes I can make to make the test case pass. So now after this change, if just I run the test case again, it should be passing. And I know that I should just add another test case to make sure that exception should also be shown if the index is negative. So I just pass in a different number here, minus one. And in this case, let me just run the test case to see whether it's failing. Yeah, it's failing, but why? Okay, I haven't handled it and then why is it... Oh, sorry, oh, okay. Yeah, you're right. So in this case, I haven't handled this particular situation. I'm expecting this function to give me our exception, but it's not showing that error or exception. That's why I have this test failure here. In order to make this pass, I'm moving on to the second phase of the TDD, which is to make the code changes. The minimum change I can make here is just to see that if the index is less than or equal to zero, I'm going to show that exception. With this simple change, I'm going to run my test case again, and it should be passing. Okay, so yeah, I think at this stage, I'm happy with the code already. So if you look at the test case, you should have, let's say, five test cases. The five test cases basically test that the first number is zero, second number is one, and from the third number onwards, it's a sum of the previous two. And you also tested the two error scenarios, one for the negative index, and one for the zero index, and all the test cases are passing. So in this case, it's a good indication that you're probably done with your implementation. There's nothing more to test, and you can move on to your next task. Are you all at this stage now? Or do you have any questions? The number of test cases really depends on the problem itself. Usually, let's say, when you write a function, you're trying to solve a problem. Let's say the problem, there might be four scenarios. So typically, you need to have four test cases, one test case to cover each scenario. And then you probably need to write more test cases to cover the cases when people give you the bad arguments or some runtime error, et cetera. So that basically means that you need to have at least four test cases, one covering each scenario and plus some extra ones. Are we all good? So let's just do a quick recall of what we did just now. And unfortunately, the session is a bit long, so you probably already forgot about what you did just now. So let's recall together. So what we did is we started from an empty project. We wrote the first test case describing what we expect for the first number. Then we write the code to make the first test case pass. And then we review, do we have any refactoring? If there's no refactoring, let's move on to the second number or second case. So we repeat this process for the second number, third number, fourth number. Along the way, we discovered some patterns in these numbers. So that allowed us to change the implementation. But each time when we make the changes to the implementation, all our existing test cases still need to pass. So basically to verify that the old behavior are kept and your new test case would change from failing to pass as well. So this is basically the rhythm of TDD. When you have a new case to handle, write a test case for it. Have a failure test case, making some changes to make the test case pass, and then do some refactoring. So you keep doing this cycle again and again. So now it's probably time for me to share with you a few rules that you should follow when you do TDD development. Strictly speaking, these are not really rules that are written into the stones or whatever. So it's more of a kind of a tips or guidelines. But for you, since you're beginners, in the beginning, I would rather you to just treat this as rules and follow them. After some time, once you're totally familiar with TDD, you can probably have your own views, whether certain rules are right or certain rules are wrong. And then in that case, it's probably more of just a tip or guideline to you. But for now, let's treat this as rules. So the first rule always start with a test case. So don't ever write your production code before you have your first field test case. So it's a rule to follow, remember. And second rule, you should only have one filling test at a time. So again, sometimes the beginners, you might have a few, you write a few test cases and they're all filling. Then you start to write your code to make all those test cases pass. So that's kind of a bad idea. So instead of doing that, you should really start from one test case that is filling, make it pass before you move on to the second test case. Another one, you should write small test. So why do I call it small test? There are a few criteria for us to determine whether that test case is small enough or not. So one criteria is the test case or each test case should focus on one particular scenario or feature or behavior of the system that is under test. And it should also take you a very short time for you to make it pass. I put a number here like less than 10 minutes, but you can put your own number there. You can see that maybe less than 7 minutes, less than 5 minutes, whatever. So the idea is you should not spend a lot of time to make the test case pass. If you spend too much time, that basically means the test case is probably too big. You're testing a feature that is too complicated. You should break that down and write smaller test cases. And you should also, for every test case, typically there should be one reason to fail. So usually that basically means that if you're writing a test case, and you should usually have one assertion per test case. If you only have one assertion per test case, typically there is only one reason why that test case would fail. If you have multiple assertions in one test case, that basically means that in general there could be different reasons why that test case may fail. But what happens if you have three assertions in your test case, the first one fails. And then the next, second one and third one are not executed at all. In that case, you never know whether your system meets the expectation of those few assertions that never get a chance to execute. So in general people would say that if you want to keep your test small, try to make sure that they only have one reason to fail. And the one practice is just to make sure that each one of them only have one assertion. And another rule, which we stated before, when you are trying in the second step of the TDD, when you are implementing your business logic to make your test case to pass, try to focus on your current test case. Just write enough code to make your test case pass and nothing extra. Do not handle whatever error scenarios that pop up in your mind, but it's not related to the scenario handling. So if you really pop up in your mind, you can't handle it right now, just drop it down on a note on a piece of paper or somewhere. Remind yourself, hey, once I finish my current scenario test case, I will move on to this new scenario and you will handle it later. Do not let the mind be distracted into unrelated stuff. The next rule, whenever you try to make the test case pass, there will be different choices. So usually the good choice is follow something simple first, and later once you discover more and more patterns in the scenarios or cases, then you can try to generalize your algorithm, or grow your algorithm into something more general that probably fit into the pattern you discover. So this just now in the example we show you, you can probably start from returning zero, and then you change the code to returning index minus one. Whenever you discover new patterns, you keep modifying or adjusting your algorithm to fit in the patterns that you just discovered. Until eventually, you discover the pattern is every number is just a sum of the previous two. Then at the time, you just evolve your algorithm to express the pattern that you discovered. Rule number six, do not forget about refactoring. So this is very common even for the senior developers. Sometimes they also get into the habit of writing test case, make it a pass, move on. Forgetting about that, okay, it's time to look into the code and then do some refactoring before you move on to the new one. If you keep doing refactoring on a code, your code will be always in good shape, which means it's easier for you to make further changes to handle new scenarios or new test cases. Next rule, remember to refact your test case too. So treat your test cases as your production code as well. So it should be in good shape. It should be in good quality as well, which means you need to constantly look into your test cases, find out the places which have duplications, find out the part that is hard to understand, and do some refactoring on it so that the test case is also easier to maintain in the long run. Next rule, which is, again, very common. So people, when you are trying to make some changes to your code to make a test case pass, you'll notice some of the ugly code somewhere and your mind will tell you, hey, shall I just do some changes to change that ugly code to make it look nicer? And no, don't do that. So basically, remember to remind yourself you should only be in one of those three stages at a time. You should either be writing a test case or you should be making that test case pass or you should be doing refactoring. Do not make the second step and the third step. Do it one by one. Make your code pass first and then you will have enough time to do whatever refactoring you like to make the code prettier or better. So those are the eight rules or guidelines or tips. So later I can share this slide with you and if you need you can probably review those eight tips later. But now I would like to talk a bit more about the baby steps. So in a few rules just now I highlighted that if you recall in the rule number, maybe the first few, I keep mentioning things like baby steps. Baby steps just have one failure test case at a time. Baby steps write small test case. Baby steps write enough production code to make the test case pass. So all these things I call them baby steps. It's important for a reason. So how many of you have played this kind of game before in school or somewhere? Like people are tied together, their legs are tied together using some private strings. And then you need to move two or three or even more people needs to move together as a group. And the first group who reached the destination wins. And I did this before and the lesson I learned is many groups, they try to be rushing. So when they start some group, they will start to try to run as fast as they can and usually in bigger steps. And many of them, they will just fall down. Or the string just broke, just break and then they violated the rules. So some of the groups I noticed that they try to be cautious. They move in smaller steps. However, they can actually move still pretty rapid. Yes, it's smaller steps, but they move rapidly. And actually in the end, for the game that I played with, the group who won was actually the group who practiced these kind of smaller steps and the rapid steps. For all the other groups who were trying to run faster, eventually they all fell down or broke some rules. So this is basically the same reason why the BB steps in TDD helps you. For every step, as long as it's a steady step, as long as it's towards the right direction and it helps you move a bit closer to what you want to do. And because it's small, you can easily or quickly make it work and then you move on to the next one. And once you get into this kind of rhythm, you will notice that you're actually moving very quickly towards the end solution. So in order to get a feel of this, you need to start writing more code in this way and to practice it. So some later you will feel that, yes, BB steps actually helps you. But then, so some of you might be asking this question later. So can we move in bigger steps? So the general advice for beginners is try not to move in bigger steps. But later, sooner or later, you will grow into senior depth. You will have a lot of coding experience. And then you will learn some of the chances that you might be able to move a bit faster in some situations. So for today, I would like you to have a feeling of that by doing that exercise again. Basically, for this Fibonacci number exercise, I would like you to delete your current project, close the IDE, delete the project, start from the new again. But this time, we can do it a bit differently because let's see, I'm going to, I'm asking you to write a code that generates the numbers from this sequence. But instead of just now, I tell you the number one by one, which requires you to really move in very small steps to discover the pattern slowly and modifying your algorithm along the way to fit your pattern. But this time, let's write it a bit differently. We will still do it in a TDD style, but I will tell you the requirement of the problem up front. So you're going to write a code to generate this Fibonacci sequence. And there are only three rules you need to implement. The first one is the first element is zero. Second element is one. And then from the third element onwards, every element is the sum of the previous two elements. So with these three rules here, with these three piece of requirements here, so if you follow TDD, you can easily start the first test case to test the first one. Second test case to test this one. And third test case to test the third one. And I would recommend you to do this again just repeat your coding exercise just now once again, but using these new requirements and then have a few. How would it be different from the experience you had just now? And for extra brownie points, you can try a different language. Yeah. But it's okay. So now, okay, let's pause for recording and I will chase you. Delete your code base. Okay, let's start again. Okay, so did you feel the difference this time compared with your first exercise? How many of you feel that you are moving faster than the previous one? Okay, some of them. You probably need to go back and then do the exercise again compared with two approaches. Basically, in the end, I hope that what you can feel is whenever you get into a new problem that you're not familiar with or into a problem where the kind of requirements is not so clear that there are certain patterns you need to discover along the way. In that kind of situations, the advice is you shouldn't move in smaller steps. So whatever the test case you write, you should make sure that you assert on a very small feature instead of a bigger one. And also for the code changes you make, so try not to write a very generic, complicated algorithm start in the beginning. Try to start with a simpler algorithm and then make the algorithm more general based on the new facts, new requirements that you discovered along the line. But there are also other scenarios like the one that you suggest now. If you are familiar with the problem, like you know Fibonacci number sequence pretty well, the moment you see this sequence, you know, yeah, it just says three rules, nothing else, probably there's a few plus, a few more error situations you need to handle, otherwise, that's it. Then you're familiar with the problem or if the problem, the pattern is well known. So if someone asks you to implement a standard well-known algorithm like a quick search or whatever, those things are standard, right? And then as long as things are standard, as long as it's well-known, then you can allow yourself to move maybe a bit faster, using a bigger, well, it may not be faster, but at least in bigger steps. So at the beginning, maybe the good as it was is always start from the smaller baby steps, because even though you remind yourself, take small steps, take small steps, your old habits will still sometimes drag you into the, using the bigger steps or even writing function calls without writing test case. The old habits are hard to kill, so you need to constantly remind yourself these few tips. Okay, so previously I planned another exercise for today, but I think time-wise, we only have a half an hour left. It's probably not enough for us to do it this time. I encourage you guys to come back for the next session, two weeks later. Again, I said it afternoon, two weeks later. We will still continue our learning on TDD. And so next time when we come here, once you guys already have the foundation, we will move on to something a bit more challenging. It could be exercise of writing a function or class, a code to convert or generate the Roman numerals, things like that. Okay, I'll give you this topic here, but I could change this for the next week. But anyway, you can try to do some exercise like this going home as well. And next time when you come back, you might feel that you are moving faster than others. Anyway, so next time, we will do more exercise to practice TDD. And for today, let's probably do some reflection. You guys wrote some code and probably some of you write the code in TDD approach for the first time. I would like to hear about your thoughts, your feelings and all questions. So we can probably discuss. Typically, I'm not a beginner anymore, so I may not feel some of the challenges or pains or questions some of you might feel. So just raise your questions if I didn't address some of the aspects clearly. It's time for you to ask and we can discuss as grouped together to reflect on what we did and what we learned. So did you guys have any surprises that didn't meet your expectation? Before you came, you probably have some impression on TDD or some expectation on TDD. Are there anything that didn't really meet your expectation or surprise you? Does anyone want to raise your hand? Don't be shy. I expected to write a lot more tests last run and write the codes to pass all the tests. I didn't expect it to be one really small test and just anything that passes this test even though it doesn't really solve the bigger problem. Great. Again, now do you understand the reason why? Why it's better to just start with one test instead of multiple tests? Hopefully, it completes. You can try. Again, the way to convince yourself is probably try different approaches and see the difference. But the easy way for me to convince you is one of the benefits for TDD is to help to drive your mind. If you only have one field test case and your mind is kind of driven by that and you focus on that particular test case to make it pass. But if you have so many test cases filling and then which one are you focusing on? Basically, you kind of lost some benefits of writing the test case first. You really don't want to be distracted. Oh my god, so many cases filling right now. Which one should I put? Just write enough tests, enough code and then it slowly progress along the way. It can be very stressful. You see all the X's and crosses in your screen. What? Victor. Okay, I used the language I was familiar with so I didn't get much surprises. Okay. But I didn't get a minor surprise. I had a hard job actually testing for error and everything. There's something new. Surprisingly it's fine. There are in-built methods at least in JavaScript that I could access to test for errors. I mean, certain errors don't have to write what matters to a certain error. That was fine. Cool, you learned something new. Anyone? Do you want to mention your surprises? Okay. It's a very disappointing thing because when I was doing it, I at least urged to skip the step and write a few more tests and then write skip steps which is what you say the trigger is that. Yeah. You are right on spot. This thing is basically a discipline and that's why we sometimes, let's say for ThoughtWorks, we promote the pair programming. You are kind of working with a pair and you are basically having each other to follow these disciplines and if your pair basically meets some staffs it's your responsibility to remind them of her. It's also your peer's responsibility to watch what you are doing and give you guidance. If you miss something, then you can be reminded. Cool, let's move on. Do you feel any parts that make you self-uncomfortable? Compare the ways that you all have it of writing code. Do you feel that this approach makes you not comfortable? Give some examples of why which part makes you uncomfortable? Picking up both like this is hard. Exactly know what I want to test. So, it makes, I don't know, I'm going to think about that rather than just implementing what this function does. But to think from a user perspective is what he wants to see. So that was a different kind of mindset. Right. But this is exactly the benefit that you will get. It just takes probably for the first time it takes a bit of time to adjust or reduce it. Cool. Personally, I'm used to writing the function first and then the test case. But in DDDR I realized that we would write the test case first. That's a bit of a challenge. That's a bit of a challenge. Did it help you that you write the test first before you write the code? Yeah, I think it's helped me because I can think more about the function earlier Yeah. It's like your function is like black box and you just give it input, this is the desired behavior or desired output. So it's like you don't have to worry about implementation but think about how people would actually use that code. How you would use it and how others could use it. Right. I think what makes me comfortable about DDD I found that if I don't know an algorithm very well but I think the first testers company discovered a good approach for writing test. So in college, everything should be written. So that helped me. Rather than having to think about it or read it and bring it around. Anything that makes you uncomfortable? I guess you have been doing DDD for some time so maybe for others who are doing this for the first time. Do you feel any past that makes you uncomfortable? Because you write the test first that when you're writing a test it feels like you're already writing a function somewhat. So it feels like you're writing twice. So it feels a little bit slow. That's a good point. For maybe for this exact exercise because the problem is actually very simple. The moment you write your test case the implementation is actually roughly the same. It's not that different. But then if you try this on different problems you can notice that your expectation and your implementation are generally different. But it's good observation. For myself when I started on TDD I used a language like Java. And if I write test case first the IDE will give me a lot of red lines in the code tell me that you don't have this, you don't have that. I wasn't comfortable with that. But later I found that actually was useful because you notice that when I started writing test case first and the IDE tells me that I don't have that class usually the IDE will give me a help I just need to press a shortcut and then the IDE will ask me do you want to create this class? If I say yes they will create the class for me and even without going the directory or the file to type in everything by myself. The IDE actually offers help whenever they notice something is wrong and then they know how to fix it for you. So actually that makes you fix the problem faster. Yeah even like the in your test you expect the integer or Boolean or whatever right. The IDE may also know oh you expect the first argument to be integer. You will have to generate a class with all that right. So you don't have to worry too much about that. So as you moving from the junior dive into senior dive one thing you need to master are the features in the IDE. How can those features make you faster and make you more productive. So things like this you probably take let's say 10 seconds or even 20 seconds to write that function by yourself but if you just use a shortcut in the IDE it just takes one or two seconds it's probably 10 fold different. The area where the test really shines is when suddenly your function needs to change you will know your test your test code will be twice of what the size of the actual implementation but why somehow or other you change the implementation of this function instead of giving you taking one argument now it needs two arguments right. So your test can easily guide you in making that change as well. If you're still past that if you're uncomfortable they can give us a one they can discuss about the part where why that would stop you to help you get started. Okay Jack, maybe how long did it take to make one test case past a few minutes? Yeah, so I guess in this case I already gave you the hint before so I guess most of you know this rule and your test cases are small enough so you can make it past also this problem is relatively simple so you can't make that path pretty simple pretty quickly but in general if you are going to try this approach on some other problems you may notice that sometimes you write a test case and the test case probably takes you one minute to write and then you found that you spend half an hour to make it past you found that there are more and more changes you discover you need to make in order to make the test case change the test case past so that may happen sometimes as well and I think maybe in the next session or in the third session later we are going to discuss about this kind of issues is very typical when people start doing this kind of TDD approach they start one test case and then they jump into the implementation and never come back the test case never comes back so that may happen sometimes so we need to talk about the techniques or tips how to avoid those kind of situations okay then sometimes people also ask do I need to write tests for every of my functions if I have 10 functions some of them are helpful functions do I need to also write test cases for helpful functions and people who are using language like maybe Java, C sharp method in the class you can have a function that is supposed to be hidden as the implementation detail in that class is not supposed to be visible to the outside world do you still need to test that function so there are questions like this so I probably wouldn't give you answer right away you discover this by yourself you can go back to read online asking this kind of question online to see what are the people's opinion so then try it yourself form your own opinion so again some questions like this there is no absolutely right or wrong answer in many cases it is based on situations but you need to learn by yourself like writing enough code then whatever you are writing called if you are in a situation and you feel that you have to test a private method ask you why why must I test this private method are there any ways I can do to avoid testing it or maybe testing this is the right thing to do so think about it when you never repeat that situation and if you really need some quick answer search on Google and read the advice from other people okay sometimes we will ask can I just write test case later it is not even a question if you have no projects definitely you will keep the situation when people just think they are very interested in solving the problem let's solve the problem test case, let's do it later even for myself sometimes I feel they are eager I hear an interesting problem I know the solution I really want to write code right away to solve the problem and writing test case seems to be and the solution even for myself sometimes I might skip this test case driven approach which should be mistakes and we should all watch each other to avoid it but then why we should avoid it because when you say later, that later may become never so once you are done with your feature make it work, looks nice on UI customers are happy then you have really less interest to write more test cases to you at that time writing test cases really seems to be extra burden it's just like maybe asking you to write more documentation you feel that as extra burden you have less motivation to do it later and also you might end up writing some code that never get used so without for TDD approach you are kind of guided your test tells you what you need and without those guidance if you just write your code right away you may build in some extra features extra functions you feel that it might be needed but in the end it's not because you didn't really think from the end user's perspective you didn't think of whether the piece of function you're writing is it really needed by them or not if you don't think from that angle you might just adding in a lot of stuff that is not needed one typical example are probably the errors you're thinking of I probably need to handle this error I need to handle that error you handle tens of errors but how many of them would happen you need to think about it before you do that if you don't think you'll probably write too many that is not really necessary and also in terms of the implementation you might for a given problem you might implement an algorithm that is very complicated that is more capable or more complex enough to solve the problem you might be feeling that I probably need this solution tomorrow maybe the current problem does not need it but I might need the problem might grow bigger tomorrow and which justify my reason to write a more complicated solution but that day may never come and those extra features extra logic you put in may never get used that would happen if you do not get yourself using the real use cases or test cases and again if you do not start with the test cases sometimes you might write code that is very hard to test later so definitely if you hit this problem in your work you will find out one day you are trying to work on a piece of code that is very fragile and so you say fine let me write some test cases to figure out what it does first but then you notice that it's so hard to test this function in order to create this function for me to test it has dependency A dependency B C D E F G then basically it's very hard for you even to assemble that function object for you to test and that's because the people who started that piece of code they didn't think from the angle of testing if they start thinking about how to make their code easier for testing you wouldn't hit that problem later so do yourself a fever by thinking about how whenever you write code how to make it testable and the best way to make it testable is starting from a test case if you start from a test case the way that you write the code you naturally make sure that the code eventually will be testable in future okay another question that people may ask you later is what is T in T D D you see T stands for test but what is test for those who are probably familiar with the more familiar with the developments you probably know that there are different type of test cases so they are unit test they are so-called component test integration test API test, UI test, E2E test whatever test there are actually many different names for test and typically I can probably give you some quick hints typically whenever people are talking about T D D they probably mean that it's a unit unit test in most cases what they mean is probably unit test but in certain cases what they mean is probably the acceptance test or end-to-end test depending on the situation especially when someone complains to you to say hey I tried T D D it didn't work, it didn't work for me then you probably should ask him why do you say that, what is the test in your case so typically people find that T D D if it T means unit test it's you really can work pretty well based on our experience T D D for unit test works pretty well in your development process but if someone tried to do T D D using the other type of test like if your T stands for end-to-end test or UI test and if you try to follow T D D in those kind of test cases your experience may vary and sometimes yes using T D D approach to write UI test or end-to-end test could be more challenging and your experience may tell you that that doesn't work well so on the internet people also you've found people have arguments whether T D D works or not then in those situations when you jump into those discussions the first thing to clarify is the unit test are you talking about the end-to-end test or system test or whatever but then the tricky thing is even for the unit test what is a unit again, there's no clear definition what is a unit some people say a unit is a function some people say a unit is a class other people actually may interpret your unit as a package or a module so different people have different in those situations try to clarify their understanding of what is the unit as well so I will stop here so these are the some materials I prepared for later sessions basically as I mentioned just now one of the challenge for people adopting T D D is to start the first test case for our situation the exercise we did just now is pretty simple it's a no-brainer to pick your first test case but in a real or slightly more complicated problems you have many choices to pick your first test case and if you're not careful you might pick up a test case as I mentioned just now take probably one minute to write and maybe you spend half an hour or even longer and you could still kind of make it a pass and we need to see some different practices or tips to handle those kind of situations to see how do you pick up your first test case so for those who are really interested I can give you a quick reference right now during the meantime if you are really interested there are two good books you can read one good book I mentioned just now is called Test-Driven Development by Example and another good book is called Growing Object-Oriented Software Guided by Test so these two pretty good books it's not new I think both are written probably almost 10 years back but they are classics so if you are really interested into this TDD approach these two books are the good materials you can read but what I can tell you is these two books give you actually two different approaches approaching basically in terms of how do you pick what is your first test case how do you start your whole TDD process these two books actually give you two different approaches so people actually on the internet call them two different schools one school is called London School another school is called Chicago School there is just some kind of name people give to them then these two schools they have different ways of patterns to solve problems one school will try to solve problems then top down use a lot of mock objects another school tend to build solutions from inside out or from bottom up using the approach with less mock objects so those two approaches roughly you can find out their route from these two books so if you have time if you have interest read those two books then you can probably find out the more details about that then for future sessions is next next session two weeks later we will come back and then do more coding exercise using TDD probably using the Roman numeral cata as one exercise and then probably the third session we are going to practice how to solve the same problem using these two approaches for the same problem we can try to solve it using this this all-side-in approach first and then try the same problem again using the other approach you can compare it by yourself what's the difference between those two that's probably our third session I guess enough preview for now to get your interest for coming back so this is the end of this session cool if you have any questions feel free to stay and you can chat with me or my colleague there we practice TDD in our project and we do have some tips to share as well if you have any questions stay and we can have a chat otherwise I'll see you probably two weeks later cool