 How do we test this? How do you know it is working? You have to write some test cases. How do you write test cases? It works. It is not working. 100 is not Armstrong, right? So if it says it is not Armstrong, it is working. English, Python, Armstrong, what problem is that? If it says false for 100, it is working. But we noted yesterday what is the problem with this? This is the problem. I cannot use this as a module because whenever I import, it will do this, correct? Some noise indicating certain amount of lack of death in the audience would be welcome. Translated English, if you are alive, make some noise. So we do not want this. So how do we handle this? How do we handle that problem? Yes, we discussed this yesterday. We say if underscore underscore name underscore that is not underscore underscore underscore equal to equal to within quotes underscore underscore main underscore underscore. If it is so, then do all that stuff. So all this quotes should be executed only when it is run as a script and not when it is imported, correct? This is the idea we said. So now if we say Python does work, now it does not print anything, okay? So the idea of underscore underscore name equal to equal to underscore underscore main is essentially what we can think of as a testing scaffold. You understand the word scaffold if you have seen buildings under construction, you will see a lot of lattice work in the outside that is required only when it is built. Once it is built, it is removed. Same way, we need it only for the purpose of testing. So underscore underscore name equal to equal to underscore underscore main is a classic canonical example of a test scaffold. So these tests are often called unit tests because they test a single unit of functionality. The idea of unit testing is to write small test code which tests one piece of functionality. Armstrong works, that is all. Now supposing some change is made to Armstrong, how do you know it is still working? If the test code has been thought properly, then we simply rerun the test and they pass, we know it works, agreed? So in the case of Armstrong we can actually write a more complete test case. How can you write that? Okay, let me rearrange that. This is a slightly more complete way to test and notice another standard practice is not show when it passes because you are more worried about when it fails and you rather print it when it fails. Please remember Armstrong is a poor example of how to come up with the test. You will rarely be able to get such a complete enumeration but we are trying to understand how to write tests rather than anything else. Now this is about the best you can do with this approach, with this scaffold approach. How do you know it works? Of course we can run it but we will do one thing better, we will create an error so that now it should report two mistakes, correct? Some noise would convince me I am talking to an audience of live people. If I make this change it should report error in two cases, yes or no? Which are the two? Which are the two cases it should report errors? Everybody strongly said yes, which are the two? 152? Which are the two cases it should report error now? 152 and 153 it reports a different error. So this is one way to write tests. We are not yet discussing test driven development. We have simply set up the machinery required to have tests written and executed. About 30% of the cases this would be enough, agreed? Is this clear? Any questions? Please look at this example as an example of the syntax and approach to use with name underscore main, not as an injection that you should try to do 100% coverage by finding out all the answers. That often may not be even possible. How do you test a square root function then? You only look at this as an example of the type of scaffold and the approach to only report failures. That is it. That is what this example should be taken for. Is that clear? Now the whole idea of testing is like I said whenever there is a change in requirements you make a change in the code. Supposing it interferes with some other code and creates new errors. How do we know it does not happen? When you write large system this is a very real problem which is why you have a large body of unit tests. So that when the unit tests are rerun we know we have not broken anything else. This means like your edit compile link cycle of C, edit compile link execute of C or in the case of using version control edit commit then you have to test. You have to automate this part. So as soon as you edit and commit then you should be able to running it should be testing it. So this automation of this testing is one piece of what we will talk about. The other is looking at the whole process of development with tests first. This is school of programming thought called extreme programming and in particular we often talk about TDD that is test driven development. I am not a very big proponent of the idea but when it works it works spectacularly and in many cases it is very useful for a beginner to a program or a domain or a problem to understand how to go about writing this particular code for a particular problem using the test driven development approach is lot simpler. People who practice that and people who propose that and propound that say test driven development is a mechanism to revise and approach the right design. It is a design methodology. It is not a coding style issue. So you do not know how to implement something. So you do not bother to just look at the blank screen or write some code. Instead you think you about what test it should pass first. Let us take an example. I will use the board. I will use this here. Let us say we want to write a function that checks whether a given number is a power of 2. How do I write it? What is the program design or the algorithm design process you would like to undergo? That is a question. And I am a peculiar Charlie. When I ask a question I expect answers. How do you go about approaching this problem of writing a function that checks whether a given number is a power of 2? Please do not give me a full blown algorithm. How do you think about it? How do you approach the problem? That is a question I am attempting to answer. Apparently we have a lot of Zen Buddhists here who approach every problem by meditating. Very good idea. If your aim in life is one up to some, if your aim is life teaching, meditating is not a very good problem solving method. Make some noise. Make a mistake. Big deal. What is square root of 8? Let us apply this to 7. What is square root of 7? Some x. What is square of x? 7. So 7 is a power of 2. Remember me in your noble price speech. If you ever prove that 7 is a power of 2, I will check whether a number is a perfect square. Not whether it is a power of 2. So the whole idea of test driven development is to give us a thinking process. Think of the test your code should pass. If your written is power of 2, what test should it pass? If given one what should it do? It should return true. Write the test. So we write code, you are happy. Test should pass. If given two what should it do? But we have not written is power of 2 yet. So if I run this code what it will say? What is power of 2? What graph are you talking about? Let us actually do it. We will use the method suggested earlier of testing for failure and we will take the UNIX approach. There is nothing to say, do not say it. So we write the test first. We run it. Name is power of 2 not defined. Now we write the smallest piece of code required to remove that error. What is the smallest piece of code I should write to remove that error? Actually our program works. So we declare power of 2 completed. Power of 2 just returns true. Which brings us to the next step in a proper TDD. You should have test that check passing as well as failing. So far you have only checked. Both are in a sense positive. Now for power of 3 what happens? For if it is 3, if it returns true it fails, correct? Or aim in life is not to have a fails. So how do we? Now you make the smallest change that will make this pass. Please note we are not trying to invent the algorithm at all. That is against the spirit of test driven development. We sneak upon the algorithm slowly. So now how do I ensure this code passes? The simplest possible code. This is a very stupid way to write code. Appears so, but it works believe me. This is the simplest possible code which will pass this, right? Now add the next case which will make the code fail. Note the idea. Write the simplest test case. Write the simplest code which passes the test. Add a test. Write the simplest code that passes this new test. Add another test which makes that code fail. Then add the tweak the code so that this new test passes. Sir are you going to write 1, 2, 3, 4, 5, some million? That is one way of spending the next 10 years, but we will short circuit the process somewhere, ok. Fails for 4. Now once you have got your 2, 3 cases down path, normally 2, 3, 4 cases down path. Now you look at what is the simplest generalization I can make rather than simply adding code. What is the simplest generalization I can make? Which makes the code pass? I want the simplest generalization of that code which will make the code pass. I am not interested in anything beyond 4. The code as it is there should run correctly. That is all my aim, but please because it now fails for 4, correct? So I want to change the code. This time instead of checking for 4 directly, I am making setting myself a new target. Instead of brute force checking, which means we will be here till 2020, can I make a small generalization? The smallest possible generalization which will make the code pass. That is not generalization. You are still counting. You are not generalizing. Instead of in one line you are adding an in. The suggestion was you can say if n in something, n in something is simply another way of writing the if. You are not generalizing the code in any way. What is the simplest generalization of this code which will ensure the code will pass? Range is another way of writing if. In range is another way of writing those. Who said n modulo 2? Yes, n modulo 2 is the simplest generalization. For 1 and 2 leave it alone. For more than 2 what do I want to do? If n modulo 2 what do I want to do? Odd numbers are not powers of. Please note I wrote the first 2 cases and added one line which handled 3, then made a change which handled that, then made a change that is all. I am not going back to the old code. Whatever code you added, but the next process is also called refactoring. I can look at the code and say can I simplify the overall code? Instead of this return true I made a generalization. Now, that has any effect. Can I simplify something? Yes I can. If you want to think about it you may, but I assert that this is enough. You do not make any noise. I do not know whether you are sleeping, whether I am not communicating or I need a PhD in Maths to come and explain this. Do not worry it is not correct. The whole aim is not to write correct code in the first shot. You do not do that anywhere right. How many times you wrote Armstrong? Now I am planning to make the mistake. It is in my control that is the key difference and I know what mistake I am making every time, but I do not bother about it. Because I know anyway I will do it. When you wrote Armstrong you do not know why it did not work. You do not know where it did not work. You also did not know when it worked why it did not work. Now I am not all this under control that is the power of TDD. Fails for 4. Why does it fail for 4? Oh thank you, thank you. That you should have pointed out then itself. Anyway okay we got it working. Now I want to add 5. So will it work now? That is the power of TDD. Slowly you will find actually you are working for more cases than you thought when you wrote the code. It works for 5. Let us try 6. Okay fails for 6. Now what, how can I, what is the smallest generalization I can do which will make it pass for 6. Please note I am not saying what is the enumeration I can do. Smallest generalization I can do that will pass for 6. I look at it and say if it is 1 I have handled. All odd numbers I have handled. Now even numbers are giving me trouble correct. Already handled all odd numbers. I can add for 7 and find out that. Okay. Are you with me? So for even numbers I make this statement. I know for even numbers I know they are usual by 2. Not a coincidence isn't it? Now I make a statement I have finished because the first if handles a one special case. The next if handles all odd numbers. The next statement reduces all even numbers to the previously handled case. Now we can change the test to do something more if you want. You notice we never bothered about the fact our algorithm was not working for part of the input. You are very comfortable with it. In fact that was the whole idea. Now if you feel bad about it that is what I pointed out earlier. When writing Armstrong also you went through all sorts of errors. So this is the spirit of TDD. You understand? Write the first few tests. The simplest test with one example and one counter example minimum. One data which should pass and one data which should fail. That is a minimum test. Write that. You are not reading the code first. You write the first two tests. Then write the smallest piece of code that will make those two tests pass. That is all. Don't think. Don't look. Refer your book of algorithms. Write the smallest piece of code that will make the function the program work for the two tests. Add a third test. Add a third test it will break the code you have written. Meaning it will make it fail. Go back. Make the smallest change that is key. Smallest change that will make the code pass. Repeat. But in the later stages the smallest change should be added by two caveats. One make the smallest generalization rather than addition. Don't add lines of code. Instead see whether you can generalize any existing code. That is the first. Second see whether you can refactor the code. Whatever code you have written. Keep adding more test cases. Declare yourself done. If your test cases are complete this requires us to think about what constitutes complete test cases. What constitute complete test cases? One would say one is a special case because it is only odd number which is a power of 2. So our test at the minimum should test for 1. We know all odd numbers are not there. So it should have one odd number test. It should have one power of 2 and one even number which is not a power of 2. This in essence is what we call a complete test suite. If you want to talk a little more mathematical language these are the four equivalence classes. All numbers can be divided into these four buckets because the test results are identical for these four. So all you need is one example from each of this. When you are building tests you need not build test exhaustively. You do not enumerate all values. Instead you look at what is the number of equivalence classes into which I can divide. All odd numbers are an equivalence class for the power of 2. How do you know that? That brain you have to bring with you. Python does not do it. When you test that bunch of brains you have to bring along. So even numbers which are powers of 2 is one equivalence class. Even numbers which are not powers of 2 is another equivalence class. One by itself is an equivalence class. So these are the four. So if you have test for all four and your code passes for all four you are done. How do I know these four equivalence classes exist? Like I said you have to bring the odd brains along. Sir how do you know what test to write? If you do not know what test to write you do not know what code to write in the first place. If you do not know what should be the result of a test for the code you are going to write. TDD people say then you do not know what to write anyway. You cannot say till I write the code I do not know how to test it. That means you do not know what to write. That is their philosophy. Sounds quite left handed does not it? But funnily enough just think back the way you wrote Armstrong. I am sure you ran it ten times and it did not it gave you errors eight times. And half the time what error you are scratching your head? Here every time we know an error is going to come and we work towards localizing that error. We produce that error controlled conditions and we remove that error and we generalize the process so that all errors are handled. A large class of problems. This is where I break party alliance with the TDD crowd. TDD crowd will say all problems. I say a very large class of problems are extremely comfortably amenable to the TDD approach. You do not know what to do. Thinking about what test your code should pass is an extremely good way to understand what your code should do. That is the real gain when TDD works. If you have not really understood the problem TDD people say writing test, thinking about what test it should pass is a good way of understanding what the code is supposed to do. What the algorithm you are still not designed is supposed to do. Are you with me? Sounds counterintuitive. But some very, very good developers and some very, very good teachers of programming share by TDD. My view is a little more, little less extreme. I find it very useful in many, many, many cases. It appears like always it is useful to decide the test cases. Is it to come up with an example where TDD may not be the best? Very, very celebrated case. So celebrated that it divided the community. One of the best known or one of the most well published authors of extreme programming and test driven development is a writer by name Ron Jeffries. He has written a few books in extreme programming. He writes a regular column. He has a wonderfully effective way of teaching through his columns. His method is take a problem and trace all the steps like I did now on the screen. He writes it in the column. This is the problem. He does not take a small problem. He does not take a solved problem. He takes a new problem and traces his progress through TDD as he thought about it. Please remember he writes more on the other aspects of extreme programming in addition to TDD. Test driven development was assured in by a school of thought called extreme programming. Their theory was if something is good, a lot of it must be very good. If testing is good, testing first must be better like that. So, pair programming is another idea they advocate. In pair programming, two people sit before one computer, one percent types, other person just looks, comments, says, but does not type. Their claim is overall productivity is more than two people working separately. So, Ron Jeffries writes a lot of articles like I said where he traces. This problem I took up. I did this. I did that. This is the code I produced. This is where the test failed. This failed. I added this. Did this. Then I paired with so and so. So, he wrote this. So, like that he has some large number of nice articles. He chose a very interesting problem. How many of you do not know what is Sudoku? So, I take it. Everybody knows what is Sudoku? So, Ron Jeffries wrote, okay, I want to write a program that solves the Sudoku puzzle. He started writing tests, small, small tests. This was a weekly column in his blog. For three months, he wrote different tests, different this, different that it never crossed a point. Then he stopped it. Nothing wrong with that. He just dropped the problem. In contrast, Peter Narvig is the director of search quality in Google. He is the author of a book on artificial intelligence and considered a big time guru. His book Paradigms of Artificial Intelligence Programming was the Bible 20 years back. He wrote on a newer book recently, 7, 8 years back, called Artificial Intelligence Modern Approach. He wrote it with another person and all the code in the first book was in Lisp because at that time that was the language of artificial intelligence. For these recent editions, he wrote it in Python in addition to Lisp. So, Peter Narvig writes an occasional column. One of his examples is Google, you type something. It says, did you mean? It is doing it very fast. So how to implement that simple spell checker? 17 lines of Python he does it, which is 90 percent does the same job. He shows it more as a type of the problems they are solving rather than to show off code. The code is a proof of concept code. So, he took up Sudoku and wrote a lovely 40 line program which works, solves in a second some 100 Sudoku puzzles types. And one Indian blogger wrote test driven development versus thought driven development because Peter Narvig will say, what type of problem are we solving? A Sudoku problem is specified by constraints and when you add a piece of data, the constraints propagate. Earlier all these could not have, should be 1 of 1, 2, 7. Now, you have added a 7 here. So, the constraint propagates and changes the constraints. Sometimes you make an assumption it is a 1 and you find a contradiction. So, you backtrack. So, Sudoku is constraint propagation plus backtracking. So, this is the way we should represent a Sudoku grid. He wrote a nice representation. Dictionaries plus this, wrote 40 lines of code. He said, if I do this, this is the ON analysis for that he gave that also. Not very serious, but you could see that even when thinking he thinks in those terms. Immediately the TDD community was very angry about this blogger saying, this is thought driven development versus test driven development. They all came back with, Sudoku is not the best of problems for TDD. The opponents of TDD which include me came up with, if the best specialist in TDD could not identify before starting to work on it that it is not suitable, how are we supposed to. So, the debate still rages, it is more than 6 years, but even now extreme programming people are a little embarrassed about Sudoku. But like I said, for a large class of problems even if you do not follow the full thing, you do not know, you know you have to write this. Writing the test it is supposed to pass first. Please note we chose rather toy examples. Our tests were one liners. In a slightly more serious problem, each of your test itself will be 3-4 lines. But if any of your tests is running more than 3-4 lines, probably you are testing at two larger level of granularity. So, that is probably a little longer answer than I wanted to give. But when you read, you will find TDD is touted as doing everything including curing cancer, do not believe it. It is a good approach for a large number of problems is probably where the truth is at. But the standard bad reaction is time making mistakes. The standard correct response in my opinion is what I gave you. You are anyway going to make mistakes. Instead of floundering around, making a mistake then trying to figure out what is the mistake. If you make the right set of mistakes in the correct order, you will do more things. Please note this way of doing it is not the way you do TDD. I am trying to explain the approach. You will use different tools, which we will talk about immediately after lunch.