 The specific tools for dealing with unit testing, there are a couple of tools which are covered. One of them is called dog test, that is a very simple tool. Idea of dog test is very, very simple. You repeat a interactive session as a comment, if you are testing something you will type something in the triple greater than prompt and you expect an answer, right. For example, e is power of 4, if you type the answer should be true. So you just cut and paste literally that session into a comment and you run dog test, dog test knows what to do. Let us look at the slide, easier to understand from the slide. You can see right beneath the function definition, you have written a triple quoted string which is called a dog string. As part of the dog string, we type the code we would have typed interactively and the answer. And then this is considered normal good practice, this is what is called a dog string. So if you do this, you remember we typed plot question mark in ipython, it showed something, what it was showing was actually the dog string. So if you write dog strings for your function, when somebody uses them they can get the help, so you write that. The idea of dog test is again very simple, as part of the dog string in the end, you write this with 3 greater than should be familiar as the python prompt. So if you have written the gcd function, you would have written gcd 48,64, you hit enter it will show 16. So you simply type that, in fact very often we do not type that, we simply cut and paste. Now you run dog test on this, we will show you in a minute how to do it, dog test is intelligent enough to, because it is convention, it will read the dog string, look for those lines which start with this and the next line, using this it will test and tell you whether you are effectively saying, if this is type, this should be the answer, dog string can parse this and run it and check whether the answer is coming, very very simple way to add test and the way to run it is this, import dog test, dog test dot test mod, that is all. If you do that, any dog test you have written will run. Let us now change this, I will save it as tdd to 2.5, triple coated string, write a command, let us say we have written this, how do we test it, first tdd to, so that that function is available, the function is e is power of 2, it is actually painful to do it this way, we will do the simpler way, import, I will skip the underscore main part, because that is only extra typing, dog test, dog test, dog test mod, so that is the type of error you will get, we will see the error. So, if there is no output that means your dog test passed, you will make an error to see understand, 3 should return false, but you have made an error, see sorry, it gives your test failed at the bottom. So, this is how it will react, if there is a error, so no, this is a very simple framework, pretty useful in our 30 percent of the cases. So, your documentation provides a way to verify whether your code is working. Like I said, how do you do that, you import, you type this, cut and paste, put it back into the documentation. This is a very simple way to implement self-contained test, advantage there is no need for this underscore main and all that, if it is there, it can be run separately. Now, the other framework is called unit test or which is lot of recipe, you have to do a lot of stuff. I will leave you to read the slides and the documentation, you have not done anything extra. If you remember the yesterday, we wrote code in underscore underscore main, you said data 0, data 1, data 2, same idea we have used. We have a file called gcd test cases dot that, we used a list yesterday, correct. You remember, everybody is looking blank as if yesterday was 500 million years away, yesterday we wrote that right, same thing. Now, instead of using a list, we are reading from a file and we are adding the data to this and we are saying append this as the test cases. So, this is essentially a recipe to do, these are the part of the recipe, you have to type import unit test, you import the module you want to test in this case gcd, unit test is standard, then this is recipe, you have to do this. Test grid test gcd function is inheriting from unit test or test case, there is a setup method, where you do everything which is done once. Then you write a function which a name is test underscore followed by your function name. If your function name is this power of 2, you will write test underscore is power of 2 and then you write this, self assert equal, assert equal means gcd of a, b must be equal to g, if it is true, it is fine, if it is false, it will complain, tear down is close the file, do everything. So, just run it, what is the advantage, till now the test code was part of the file we were testing, correct. So, we said if underscore, underscore, name, underscore, main and all that, so we were cluttering up our code with a test. Unit test provides for a nice separation of the test code from the code that is tested. So, your test code is in a separate file, but which code corresponds to which function that is understood by this convention. Whenever there is a function name starting with test underscore, unit test will look for a function with the name bar which is remaining and then do the right things. This is mostly convention and as procedure to do certain things, nothing much to learn here, you simply have to follow the procedure. Procedure is import unit test, import the module under test, write this, create one class, setup part is specific to the module because this is where you are describing the test data relevant to the module. If v is power of 2, there will be only 2 values, one test value and one true or false value. For GCD, there will be 3 values, 2 arguments and the return GCD. So, the code will change appropriately test cases dot append, test cases dot append, test cases is a predefined variable, everything is once it is done, you write a function test underscore is power of 2, you will say case self test cases a equal to case of 0, b equal to case of 1, self dot asset equal is power of 2 dot is power of 2 a comma b because there are only 2 values and the b values are supposed to be true or false. So, your file will contain true or false, this is wrong. So, you will have one unit test file for each function tested, you can have multiple unit test functions for each function tested also. Sometimes that may be the best way to test, normally the ratio for each function may even be you may have 3 functions testing a function, 3 test functions testing a function that is perfectly ok because that may be the cleanest way to write. Like I said there is nothing much to learn here except what are the rules, this is near mechanical, if you use a decent IDE it will do most of itself, you just have to fill in the blanks. There is lot of detail, so do not get lost in the detail, understand what this buys you, in the first case we wrote the test code as part of the module itself. So, that is not really useful then we came up with the idea of this scaffold underscore underscore name underscore underscore main, still if you are writing a lot of tests for a function you are carrying around a lot of extra baggage, why? Whenever you use GCD that still will be imported. So, unit test gives you a nice way to separate the test from the code that is tested. So, you can improve the test independent of improving the codes. So, whenever you make a change to the code you run the same unit test. So, codes pass your change is good. Typically the way unit test grow is somebody files a bug report does not work for this input. So, you look at it and say oh yeah this is the mistake. So, you write another unit test which test for that sort of data make the code change add to the unit test and run the whole thing. So, that the function now has the new functionality plus the earlier unit test guarantee you are not broken existing functionality. You understand? Shall you all act as if we understand? Since you do not want to acknowledge we understand let us at least act as if we understand and move on. Fine, we declare this as understood. So, these are the components setup is essentially where all the data is read and it makes sense to have a separate file. Because if you want to add some more data you should not be editing code you should only be editing a data file. Put setup's job is to read that data and load it into a list. Tear down is to delete the whatever data items use so that memory is free. Test GCD is the actual test code. Assert equal is a new function that is provided by the unit test module. In fact the whole idea of unit test is to have a lot of asserts. You may say assert equal assert not equal assert greater than assert less than you have all those. This is the other thing unit testing is a good thing. Test driven development is writing the unit test first. You may or may not use the unit test approach of putting it in a separate file. Do not confuse the idea of unit testing and test driven development with the tools unit test. Unit test is a tool given to capture and execute unit test in a comfortable way. The idea of unit testing does not depend on this module. The ease power of 2 wrote we did not delay on anything else. So, how would we have used that? After we are happy would have deleted all the test code and just given the ease power of 2 or if you are a little protective we will save it with a test code make a copy without the test code and distribute it which is ok for small functions. But in a typical full fledged application environment what will happen there will be hundreds of functions and hundreds of files. So, if you are going to remember to delete and do you are going to have a full time doing that. So, separating into 2 ensures this is done comfortably. And you can also put these in a separate directory the code in a separate directory and everything will be taken care of by unit test module. Understood? There is only a procedure and some recipes to be learnt here. The concepts are the same as what we did. The code you write is still your business. What are the unit tests my code should pass that is where your thinking comes. This is simply extra fitting to make some of the bookkeeping easier. It does not add to your ability to do anything other than doing bookkeeping easily. Can we move on? I want to drive down to a next conceptual thing. Let us revisit our front arms strong. Now we have written some tests here also right. But something is fundamentally wrong with these tests why we can write this test only if we know everything about the answer. How often that is true? If you know all the answers where do you need to write tests? So, do we say oh then arms strong should not be tested is it likely to be a logical conclusion? Is it a logical conclusion that arms strong should not be tested or cannot be tested? What do you think? 230 is not a time for thinking is it? So, what is the problem? So, this whole approach is wrong. But how else can we test these arms strong? You understand why these tests are ridiculous? This is like saying you can solve the problem after you have solved it. In order to solve the problem you have to solve the problem that is very interesting way of defining it. But that is exactly what these tests are doing. In order to test the problem you have to have successfully run it. But how do you define successfully running it without having bugs? How do you define not having bugs? We have tested and found not bugs. So, this whole business is pure bullshit very good looking bullshit if I might add but it is still bullshit. So, what does that mean? It means the way we have written arms strong is untestable. That is what we often talk about testability of code. Is the code testable? The way we have written arms strong it is not testable. The only way to test it is by running it producing the right answers and verifying whether it is producing the right answers or by some other means knowing the right answer. Then why are you writing this? If you know the answers are 153, 370, 371 and 407, why are you writing the code? Might as well write a program which print 1753, 370, 371, 370 right? Because you know the answers already. You understand what I am driving at? This whole test is nonsense. But how do we then test? Or do we say no point testing? I am suggesting neither of these is the right corollary to derive. The right corollary to derive is boss the way we have written arms strong is bullshit. It is not testable and as such bad code. It produces results. How do you know? Because we know the results. What happens about a program for which we do not know the results? Let us remember the word is unit testing. The whole idea of the function is to generate arms strong numbers. We are not doing unit testing. In other words our arms strong is too big. There is no unit to be tested. In 6 and a half lines what is too big is the next logical question. Am I communicating without meaning to sound arrogant? This is the real value you will get by having a trained for having an experience developer teaching you. So, I seriously request that you put in a little extra effort and listen. This code as an answer in an exam will get marks. Why? Nothing wrong with it. Because exam questions you know the answer already. So, while the power comes back let us hammer that point a little more. In a production environment you do not have the luxury of knowing the answers. And if you know the answers you have the luxury of not having to write the code. Only in college you would write code even when you know what the answer is. In a production environment if I know these are the four arms strong numbers I will be an idiot to write a code. If I have a piece of code how is it testable is the interesting question to ask. So, which is why I am using arms strong as a pointer to this does not give me testable pieces. This gives also the whole problem. In other words I was asked to generate arms strong numbers. Now I would say you know what we can generalize this is for three digits for four digits we want to generalize. So, we test with three digits then we assume it is correct for four reasonable answer. A more useful answer is to say that is correct but can we find a way of improving the testability of this code. One of the good spin-offs of the last 10-15 years of software development advances is this concept of writing testable code. We often talk about readable code, thisable code, thatable code. Testable is a new ability testability is a new ability that is a nice thing that has come. So, often if you see a piece of code and you do not see a way of testing it clearly it could mean one of two things. You have not understood the problem enough or you are not comfortable in TDD possible. More likely you have not designed the problem to the right degree of granularity. So, we will use arms strong once again as our example. So, this is the code we got and we want to write it more generally. We want to write it how more generally I want to generate arms strong numbers from A to B, A B given. So, 500 to 27,200 return a list of all arms strong numbers which are between A and B. If 527,000 are given no 3 digits should be returned, some 4 digits should be returned and some of the 5 digits should be returned. I want to write code like that which means I have to break down arms strong further. How do you break down? What is the idea of breaking down? Writing it in testable chunks. How can we do that? Anybody has an idea? Again 240 is not exactly the best of time to have ideas. For the record I am writing code from 1985 and I am teaching approximately from the same time. Lot of people have learnt by making mistakes. Lot of people have learnt by arguing. Lot of people have learnt by discussing. I do not know of even one student who learnt by keeping quiet. I humbly suggest you are not going to break that record. So, start talking. So, start saying something. People have learnt by saying nonsense, but nobody has learnt by keeping quiet. How can we break down arms strong further? We can use a far loop. How does it change matter? Then we can give the range and… That is for getting arms strong. I am saying I want to test this function. So, break down this arms strong. How do you do that? I am making a claim this arms strong as written is too big. Wonderful, but how do I break this down further? You are adding conditions to the code. You are adding conditions to test for the code, for the code to be applied. I am talking about this code itself is too big. Samson's comment was if I understood him, something along the lines of whatever range, something like that for some range. Agreed? That is how we ultimately have to use it. If I understood some years comment correct, you have to come in and check whether n is less than 100, etcetera. Am I right? True. If I am looking at what is called adding gods to the code, what you are saying is right, but you are not simplifying arms strong any further. I want to break down arms strong. I am making a claim it is too big a function. It is doing too many things. That is why you are not able to test. Find the number of digits. That is a nice SMS. Can we hold a conversation instead? Look at my beard. I am not the SMS generation guy. I like to talk. Translated English I am saying expand your answer. We need to find the number of digits in n. Very good. Instead of putting that 3 as a magic number, we can improve the function, though we are not breaking it down by excellent starting point. But instead of breaking down, it has put on some more weight. But yes, this does generalize nicely. We are going there. Now combined with Samson's idea of the far loop, we are in reasonable territory. We have a number of digits of course. Presume it is writable. But now we already have a piece. Now number of digits is testable. We have added one testable piece. The idea is exactly that instead of thinking in terms of arms strong as a single monolithic entity, can we think in terms of pieces? How do you do that? First rule or one of the most important rules is writing good code. Never fall in love with your code. Delete your existing code often. You will write better code. That very thing is confusing our approach. You are looking at it saying it looks nice. It is working. Little here, little there only we are doing. So delete it. Now let us think of priority. What are we trying to do? Samson's idea is a good starting point for n in range a, b. Somewhere a and b will appear by magic. What do you want to do? You want to print Armstrong number. Good. Let us stick to the old example. Of course, we can add make a and b read from a file or accept from the user or anything. Now we have to write is Armstrong. We already wrote it. Yeah, I know. But we are going to write it with a slightly more optimistic viewpoint or slightly more lazy style. Translate into English. Armstrong definition. That is nice. Now we have broken Armstrong into two pieces. There is a piece called sum of cubes. There is a piece called digits. So digits should return what? Should return a list of numbers. And sum of cubes should cube the individual numbers and sum them. These are testable because they have an answer for all possible numbers. These are testable. Once you do it, you can test digits separately, sum of cubes separately, then rest assured these Armstrong is working without having to exhaustively test these Armstrong. So what about digits? How do you write digits? We will call it number to digits. We have to, we agreed we will return a list, correct? So we need a list somewhere. Understandable? Testable? Separately? We can do anything we want. We can use our unit test, doc test, whatever you want to test this. I am not going to test it because my goal is to teach you the approach, not run test. Now what about sum of cubes? Sum of cubes is testable? We will generalize in a minute because we have written sum of cubes. It has to be 3. We want to write general Armstrong but we are sticking to 3. Shouldn't we fix it? Yes, we have to. So sum of cubes is not correct. We have to write sum of powers, correct? And what is the power we want? Name has to be changed. Are we better off compared to where we started? We ended up, we ended up with some 20 lines of code where we had 5 but surprisingly we claim we are lot better than where we started. Two reasons. Now our code is testable and we have some two nice pieces of code which we can reuse. Num to digits is usable in other context also. Same for sum of powers. If you remember yesterday I said that code will get only 4 out of 10. This will get all of 6 out of 10 because it is still terribly written. It is not good Python code because it does not use many things which Python can do but my goal is not to write high fund of Python code. We show it to you in the end for comparison but I believe this code is wonderful as it stands. It is readable. I don't see anything which requires explanation. Is there in the code? None. And we have two pieces of code which are testable by themselves and reusable. If given another problem which deals with numbers we are very likely to find these useful. Where did we arrive at this gem of an idea by thinking about testability of the code. Whenever we have written a chunk of code which is much larger and a unit testing approach seems to be doing a bulldozer or chicken and egg problem in order to test it we ought to successfully run a type of situation you end up with then you think and say maybe I have designed it wrong. Can I design it into more testable chunks and genuinely you are doing unit testing now because these are the units of functionality you are testing. And note that these two have nothing to do with Armstrong. There is nothing in sum of powers that ties it to Armstrong. That is a good definition of a unit. Similarly, there is nothing in number two digits which ties it to Armstrong or Lex Armstrong or anything of that strong. Are you with me? I hope the CS people have something interesting to take home. You could use this as an example of functional decomposition if you want. But to me it is canonical message is testability is a lovely guide to look at the program design you have done and see you know what you are thinking into big chunks. We all keep saying we should write reusable code easy to understand code. That is all nice in theory. How do you write in practice by looking at the program design test it as a does it do a job, a small job and only that job. More importantly in my opinion this is a lot easier to explain than the code we wrote. It is a lot more code. Do not fall into that fallacy. Typing speed is 40 characters per minute, 40 words per minute. I mean type is suppose type 80 words per minute. Let us give ourselves half the speed. That is about 8 lines per minute, 1 minute versus 3 minutes. But the actual time you took to write the original Armstrong was how much? Half an hour. Now thinking this way you would have done it more correctly, more reusable in probably 15 minutes. Yes or no? If you are thinking in these steps you would have done Armstrong in 15 minutes. Lot more correctly and ended up with lot more usable code. What pointed us in the right direction? The fact that as written Armstrong was not really amenable to good testing. Am I communicating? Can we move on? Do you have any questions? And usually when you write like this I will make a arrogant statement doesn't need to be tested. This function works as stated. Why? Because like I said this is not the canonical Python code by the way. Like I said never fall in love with your own code. So you want to see actual Python code I might want to write. That is the 9 out of 10 Python code. That is lot more Python than you want to know. And compared to the previous code this code has one fantastic property. What is that? Do you see any variable whose value is changing? x equal to x plus 1, n equal to n by 10, s equal to s plus p cubed. Do you see any such variable anywhere? All variables in this code are immutable. There is no variable because there is nothing is varying in this whole code which makes it lot more comfortable to reason about the correctness of the code. That I would probably check in somewhere else but yes that is the point. You want absolute value yes number to digits whether it should have yes I can add a guard. I can add a guard for that but let me stress this is what we call functional programming style. Zero variables which are varying whereas zero variables. See sometime back said you have got lot more lines of code that is okay. Now even that problem is not there. You probably have as many lines of code as earlier. I will sincerely suggest typing down this code is not going to improve your ability to write. Some is a built-in function which returns the sum of any list passed to it. The expression of x for x in p is what is called a list comprehension. Returns a new list. So like I have probably already told you a couple of times the nicest point about this code is it is functional. No variables. So it is lot more easy to reason about it. Lot more easy to verify that it is doing what it is supposed to do. Which brings us to another wonderful advantage of Python. It is equally easy to write nice procedural code which is just now dead and nice functional code. Both are perfectly easy to do and it is a choice of your way of doing things. Actually bolt all this to a class and make these methods and call it object oriented style. Most of the IT companies they are having some coding standards. So these standards are such useful at that point of time. So that the followers next generation to modify the code will follow the standards for that. Since that is what I do for a living. Train fresh, software engineers to work in big ticket companies. Yes. That is one of the big things. Taking a small personal digression I use Python to teach programming logic. That way people can forget this intax. Because where is this intax here? Even if you do not know Python this code should be readable. Definitely the previous code is readable even if you do not know Python. I would suggest even this is readable. So you can focus on solving a problem. And writing reusable code. Yes. Testability is something which all big enterprise companies want. Because they want plug and play employees. If you are not there you should be replaced by somebody else. Which will work only if the pieces of code are self standing testable and assemblable like this.