Lesson 4 of 21
Introducing TDD
A short demo showing Test Driven Development
In this lesson
Roy Osherove gives the three rules of test-driven development (external link, opens in new tab): start with a failing test, make it work in the simplest way possible (external link, opens in new tab), then make it better. He adds a subtract method to the string calculator (external link, opens in new tab) test-first, choosing simple values such as one and two and deciding the order of the parameters while the code does not exist yet. He makes the test fail by returning minus one before implementing the method, and shows the IntelliJ run configuration that runs all JUnit (external link, opens in new tab) tests in the package.
Transcript
[00:02] (external link, opens in new tab) Let's talk about the difference between unit testing (external link, opens in new tab) and test-driven development (external link, opens in new tab). If we talk about test-driven development, what we want to do is to actually say okay, I'm going to write the test before the code. And the three simple rules of test-driven development is that you start with a failing test, you make it work, and then you make it better. So you have to start with a failing test. You should not have any piece of code without a failing test. The code is read only as far as you're concerned without a failing test.
[00:34] (external link, opens in new tab) If all your tests are passing, the code is read only. And then when you finally have a failing test, you have to make it work in the simplest way possible (external link, opens in new tab). And what we'll see is the simplicity is not as simple as you think. Simple code is actually very hard to achieve, especially if we have been conditioned to work in a very generic way our whole coding lives. Once you make the test pass in the simplest way possible, you make the code better because you made it pass in a very simplistic way.
[01:06] (external link, opens in new tab) Then you make it better, more maintainable, more readable. All these three rules together make up test-driven development. Along the way, you achieve better design, you achieve better usability of your code. And you understand the problems much better. So let's see what that would be like. Let's say that I I'm going to start with a whole new project. I'm going to say file new project create a module. I'm going to say
[01:40] (external link, opens in new tab) TDD demo you know what instead of creating project let's not waste your time. I'm going to add a new feature to the string calculator (external link, opens in new tab). Okay. And the feature to the string calculator will be let's say that every time you call we're going to have a divide method for it. Okay. And divide will basically divide two items one upon the other. So the way to do that is not to go to the to the string calculator implementation but to start with a failing test.
[02:16] (external link, opens in new tab) So, I'm going to write a test right here. public void. Now, I have to start thinking about the name. Now, the method doesn't exist yet. And that's kind of the nice thing about TDD is that the method doesn't exist yet. So, we get to name it whatever we want.
[02:51] (external link, opens in new tab) Remember I want to divide something. What should we name the method? Divide. Very very in insightful. Divide. And then I have an underscore because I want to test it with some scenario. The first scenario I want to test is the simplest scenario that I can do. For example, I'm going to say divide two simple numbers. Two different very simple numbers. And what's what's supposed to happen?
[03:28] (external link, opens in new tab) Divides them. Okay. I might say make calc extract variable and string calculator dot divide. Now divide doesn't exist yet. So I could so I get this nice little red sign. Now what are the two simple numbers that I can send in? Remember I want the serial killer to not come to my house.
[03:59] (external link, opens in new tab) So, I want them to ask as least questions as possible. What numbers will make them ask less questions? Okay, let me let me make this even simpler. Okay, let's change it to subtract. Okay.
[04:29] (external link, opens in new tab) What two numbers are the easiest to show that subtract works? So, as I'll give you a hint, it's usually the first numbers that will make you ask things less. For example, if I can give any number between zero and 100, one is better than 42. Why? Because one is the first one. It's just above the line of what makes a good number. It's just the first one. So our mind is is is usually capable of detecting patterns.
[05:01] (external link, opens in new tab) If I send in one and two, it's better to sending in 45 and 76 because someone looks at 45 and 76 and says, "Yeah, but why? What's special about these numbers that you're sending in these numbers?" and also it's much more difficult to subtract in your head. Okay. So it's better to use the simplest forms and the simplest values that you can think of. So what number should we send in? What should we send in guys?
[05:34] (external link, opens in new tab) >> One and one. What happens when we send in one and one? Are we going to get more questions or less questions from one and one? more. Why? >> Because the same number. >> Because they're the same number. Exactly. Looks It looks It looks special when you have two of the same thing. Okay. >> In this case, >> yes, that's true. So then how so which one would you rather have is it's not actually simple because then you don't know what the order is,
[06:13] (external link, opens in new tab) >> right? It doesn't matter which order they are. >> Should it matter? >> Yes, >> of course. >> true. >> Okay. >> True. Okay. So, let's do that. Result in result equals Okay. assert equals
[06:44] (external link, opens in new tab) What do we expect the result to be >> or do you want to change the order? What do you want to do? >> Well, you have to decide whe >> Exactly. So, I'm making the decision now before the code even exists. I making a design decision about the usability of the API. So I can just decide and say, you know what, it makes much more sense to me to send in subtract one from two and then assert that I get back one,
[07:16] (external link, opens in new tab) Right? But if I said subtract two from one, then I might that might feel a little bit weird for me to say, oh, I would get back minus one because that doesn't feel natural for me to read. So now you get to feel like what it's like to be a user of your own API before the API exists. And if you don't like something, you can change it because the cost of changing it is zero because the code doesn't exist yet. That's kind of the nice thing about TDD is that you get to see what your API will look like before it exists.
[07:50] (external link, opens in new tab) And you can get to play God and you make the API that you wish you had because we can make any decision we want. Now the code doesn't exist yet. There is no cost. So we can say subtract one from two and we can expect one for all result. Now we've made a design decision about the order of the parameters. This is important because if we would have written subtract before the test, we might not have cared about the order in terms of usability. We would have cared about the order, but we wouldn't care whether it was usable or make sense to the read user of the API.
[08:26] (external link, opens in new tab) See what I mean? You we could have But we wouldn't be have it wouldn't force us to face the choice as a user which we do want. So one of the nice things about tests is that they allow you to feel like a user of your own API. So now that I have the test and notice that I'm not making a compile and then making the assert. I'm writing the whole test and I see the whole thing. And only when I'm happy with how the test is written and I like the API then I start making it compile.
[09:00] (external link, opens in new tab) Now I'm going go and says I'm going to go and say create the method int subtract int how much oh there how much from return from minus how much
[09:44] (external link, opens in new tab) Sorry, I can't hear you. Where is divided? >> Up there. Divides them. >> You see this is where pair programming (external link, opens in new tab) comes in. Subtract them. Now I'm going to run all the tests. And the way I did it, I just created an old test build that run all the tests in the package.
[10:18] (external link, opens in new tab) Is press shift F10 here. H. And if you if you are using IntelliJ the way I did it is by editing configurations and I pressed the plus here and I created a new JUnit (external link, opens in new tab) one and the test kind is all in package in the whole project. That's all I did. I give it a good name. All tests dude or gal and then I just run that.
[10:54] (external link, opens in new tab) Okay. Now remember that I made it compile but I didn't make it fail. So I didn't actually practice TDD now. So what I'm going to do is I'm going to say return minus one. Why minus one? Because usually when I think about something illegal or something wrong, I just return like a minus one to show that there is a real bug in the system that I would never accept. And then I just run the test again. And of course now it fails. And I can see expected one actual minus one.
[11:29] (external link, opens in new tab) So for ins I usually just return minus one. So I can see the test fail and then I go and I fix it and then I should see the test passing. So now I've tested the test. Now I can go and refactor if I want to if there is something to refactor. Although I kind of like the names that I have right now. It's very very simplistic. I haven't added a lot of things yet. But the point is that I started with an API that doesn't exist yet and I made it work only after I was happy with the API that I'm designing.
[12:05] (external link, opens in new tab) So during 3D dur during TDD you might change your APIs quite a bit. Okay, questions so far. And of course this is a very simplistic view of TDD. We're going to talk about mock objects (external link, opens in new tab) and stubs (external link, opens in new tab) tomorrow. But this is about 95% of what you need to know. Without mock objects. It should be easy. It should be this easy because if it was very complicated to use a test framework then many people wouldn't be doing it.
[12:35] (external link, opens in new tab) And kind of the point is that it should be easy to practice. But I want to talk about several questions that usually happen when I show test-driven development. And the the TDD that I showed is about the string calculator which we will do after lunch today.