 Yeah, okay. Welcome to my talk on unit testing and TDD and Testing is an important subject and there should always be on every conference lots of talks about it and in for all programming languages on the other hand there have been already a lot of talks in a lot of conferences and And I don't know if I can say something truly original about this. So this talk is aimed Mostly at at beginners in testing But also for people that have tried testing a lot and somehow struggle with it found it difficult to adopt Simply dissatisfied with with with the success that they have with it The general structure is The why the when not and the how know it's a bit different than in the title. It just sounded better in the title and Let's let's start with basic reasoning why we would do testing at all and The thing is what do we do as coders? We we read a lot of code and we read a lot of code and we just continue reading and Only after reading a lot We will also write some code and usually the point at this time in any presentation is The code should be readable because we read it much more than we write it But I want to add a third thing that we always do and that is the trying out afterwards see run the code and see if it actually and I I hardly ever commit any code without at least trying out without looking at it at all because when I do I Always manage even even in a single character change. I always manage to put in some some New error, so at least I I try to compile it. I run it in some form it has to be done and And when these this phase of baby steps Baby sicknesses is over and you you know your code runs then you feel this feel like dr. Frankenstein It's alive and you'll be really happy about that And the very important thing is you can be your own beta tester You you simply get a feel of how the code works and Unfortunately all all future techniques that I will build on now everything like automating tests and TDD will not give you this But I'll come back to that later one of the dangers of maybe Trying out is shotgun programming as as humans we tend to try to keep the thinking as at a minimum and I I catch myself frequently that in instead of really thinking how to solve the problem I just try it out. Oh, it doesn't run then I wiggle some variables try it out again And I just continue this without actual thinking until until it looks good enough The the other extreme is maybe a little bit older the at the time of Punch cards and maybe nowadays if if you have a slot at a at a set time slot at a super computer I'm sure you will think for hours Over your code to make sure that the one shot that you have at running the code will be the will be successful But what is the right thing to do? Should it be a lot of thinking and hardly ever trying out or should it be a lot of trying out and hardly ever thinking? Let's get back on track on the why let's automate and and trying out is nothing really else than then automated trying out and computers are very good at doing repetitive tasks and Especially when the setup is Expensive I imagine there for example you you have to go manually into the the jungle admin or Into an SQL console and tweak some records and remove all the traces from the last 10 test run Then you go to your actual application Click click and a number of buttons to to do the the manual testing to do the manual trying out And I bet with so many steps in between you occasionally will just make some errors And you have to start again go back to the SQL console or remove all the traces return to that state and Automating it scripting this part is definitely worth it and the problem with documentation is that it out dates and Tests good test names are very good documentation which Automatically raises an alarm as soon as it out dates. So when the code changes in some form The test will break and maybe you decide well this test is actually outdated. I'll change it or I'll remove it Okay, now. This is a bold statement to say that code becomes reusable and modular people say that but but How does that happen? Oh This is a bit too big now Well, I hope you can still see it This this this code snippet it it does what it's supposed to do So it normalizes the input key and then checks if that key is in some cash storage Otherwise it will return a default value So we are done right and the code works. It does what's supposed to do but But it's really hard to test Why is it hard to test because it uses a lot of outside dependencies and One thing that you usually do is you you make dependencies injectable in this case you would move the the store from the functional scope that we have here one scope further up into the object scope and Then the test can easily inject those dependencies And the function Excesses it through the the option to the to the object scope Another thing that you realize when doing tests is The the the the standard dictionary has a get the get of the standard dictionary accepts an optional parameter for the default and that's a good pattern and in this case it also helps with testing because you can Simply put it as an additional parameter into the function call instead of having to mock the get default function in the object and Then while testing you might also realize that this Normalizing of the function is really its own the normalizing of the key is actually its own functionality And you might want to extract it so that you can build Targeted tests for this one and targeted tests for the the getting out of the storage so What testing really does is it testing reuses your code and And by trying to have small test cases you are reusing it bit by bit and In order to make your code testable you have to make it reusable and you have to make it modular So testable code is good code Let's get back and see What what happens if we don't just write a few tests? Just just maybe the happy cases and maybe one thing but really a comprehensive test suite Which means that you try to go through all the error cases. You try to obtain as much code coverage as possible Well as I just said if if you care for all all Error cases and for all branches then your code becomes more robust Because if because it's not a thing that you normally try out You some some things are even impossible to try out through the UI But they still somehow happen due to mysterious circumstances and in automated tests you can You can produce these weird circumstances and That way test all cases which makes it more robust and once it's more robust You have protection against regression and only comprehensive test suites give you that just a few tests here and there are no no protection against regression at all and Regression is maybe a thing when you change a small function somewhere if you if you work on a single ticket But my favorite thing is that I'm very dissatisfied with one module I go into that module and turn it completely upside down. I can refactor the whole thing and When I have if I have these comprehensive test suites Then I can be sure that my changes then I can check afterwards What effect do those changes have on the other parts of the of the system? and And I have to say this feeling of freedom that is the best feeling that I ever get during my development I'm I'm I'm just happy when I see that So, okay, you say I write tests, but afterwards so Why even bother with TDD? What's what's the point in moving the the testing in front of the development? Well, there are some risks in testing and What I have written so far what I have said so far you can find it in any book But the books maybe don't mention all too often also the risks Especially because accomplished testers don't really have these problems anymore an accomplished tester might say What kind of moron says that he's going to make tests, but then is not doing them In my experience it is it just practically happens that out of all kind of reasons Not being clear about it sometimes just being lazy you're not doing it We can assume maybe people have talked about it often enough that That writing good tests Is is profitable because because you gain more time Then you lose writing the tests Unfortunately, you will not only write good tests. You will also write bad tests and that also takes time Which which you don't get back and in fact once you have those bad tests and You you will pay interest you pay interest to update them or you pay interest to to live with them And what I just said that tests make refactoring possible. They also discourage refactoring. I once had I had extensive tests on my views like a hundred and twenty test cases on the views and The test relied on a certain way that the views set up there the initial data But that that way how they set up that was just Deprecated and I wanted to change it so I wanted to refactor it so That the views get their setup data from someplace else, but that would break all the tests So it took quite some time I postponed it a lot Because I was simply afraid of breaking all the tests and having to rewrite all this stuff again and With all things if you simply don't do it right then you don't get the benefit out of it I said the benefit of good test names is that they document if the test names are not good Then they don't document and this has something to do Sorry, this has something to do with with the robustness of the mythology Testing itself doesn't seem to be a robust because The worse you do it the less benefit you get a robust system would be well you do it halfway correct And you still get good benefits but That's not the case I've been talking about shotgun programming shotgun debugging is is the the scaled up version of it That you don't really have to understand the code at all. There's a box somewhere or you you have to add a new feature Test test break. Well, you just wiggle variables so long and you run the test and wiggle variables and run the test until it works No thinking involved So Yeah, as I said, this is a question about robustness and Moving now the testing from after the development Before the development it doesn't solve those problems But I have found out that it gives us a certain amount of control over them So laziness and time pressure leading to not writing tests Actually TVV doesn't help so much with that You can you can just as easily not write tests beforehand as you can not write tests afterwards But then again postponing the testing until a time when the developer has somehow found closure when the developer thinks now the thing is done That maybe is just not the optimal time to put their testing in the process. So Maybe TDD helps us a little bit. I show that by some arbitrary number. This is really absolutely arbitrary number It just says well, maybe it helps us a little bit. It gives us a little bit of control and bad tests Whenever Whenever you do two related things Then you do the first though the one that you do first you do it twice so when we do test after then we do the code and While while doing the test we go back to the code and update it a little bit And and there we have actually the thing that that tests help improve the code quality on the other hand if we do the test first and And the coding after then we are working for the tests twice once when we write it and once when we are actually Doing the code sometimes maybe we realize oh the test was a bit wrong and we have to update it so which one is better and I think that And We have to to see the priority the the most important thing about code is that it runs correctly The second priority of code is that it is of good quality. This is maybe weird for developers But in in an economy in the economy it is in fact so that if you have running code independent of the quality Maybe that is the time when you start making money and that is necessary for anything to continue and also We have processes we have refactoring and the tests help us a lot to later Update the code quality to become better. We have the tests to help us in the refactoring on The other hand it is not priority one of tests that they just pass It's not the objective that we have many tests that pass The objective is that the tests are of good quality that they are testing exactly what we want to do So I say the most important thing at the time of creation is that we are thinking about tests twice so we do it first and then we think about it again to ensure good quality because unfortunately there isn't really This this third instance the other thing which helps us refactoring the tests. We are a bit on our own on that one so back I would say that gives us quite a TDD gives us quite a lot of improvement on this one and For for the refactoring TDD actually literally is the answer because what you don't want to do What you don't want to do is? What I do in the in the test after a mentality what you do is That you you first go into the code You you refactor the code because it's bad code and then you break all the tests Well, okay, and then you go into the test and then you change all the tests But not only is this dreadful work, but also you're losing all the benefits the tests don't help you anymore They don't protect your refactoring so literally the the solution to this one is think the other way around think test first Go into the test first refactor the test first to to be able to do both The new and the refactored version of the code and then afterwards refactor the code and that way you are protected all the way through So even more imaginary point on this one and with test names, it's the same thing really because When you are testing first then the very first line of code that you're writing is the test name So the very first thing that you do is when you start Articulate what you are going to do next and that's how you get good test names On the other hand if you have the test after mentality if all your code is already there And you just copy paste a lot of test cases then you just You just have to fill a void with necessary identifiers the compiler says well those tests need some name And you just say then test one test two test three test four test again and so on So I would say TDD gives us even more control on that But there are still 2k 2 points that I skipped so far and well, they are really difficult you need You need understanding you need to understand your The the requirements in order to do this well As I said about shotgun debugging shotgun debugging is the opposite of understanding. It is the not thinking so TDD helps us with With thinking because it it focuses on the what and not on the how in fact, I would say When when you are at this place that you want to write a good test is the first thing you can't really do that without Without understanding what this is about because you have to formulate a test name and you have to to create You have to create the what you have to articulate it not only in the name But also in the in the in the test code and at this time your understanding becomes deeper On the other hand unfortunately, it is possible to just put there some code which Which seems to be like maybe what the customer wants and It is always very easily possible to just put some tests afterwards which pass Writing tests that passes is very easy But that's not what we want. So the control that the gives us here by SS in Germany We would say understanding helps you to overcome your inner pig dog But let's have a small intermission. Why is testing especially good for pythons? In some linked in post unfortunately, I I didn't write down the source So I can't give you the source but in some linked in post I saw like nine or twelve a Matrix of nine or twelve of these kind of diagrams which plot Efficiency that you gave that you have in a language as a function of the experience that you have in the language And I distinctly remember that there was a significant update in Spike in efficiency due to unit tests and I was wondering and I mean it's true I felt this myself, but but why is that so for Python and not for all the other programming languages? In my opinion one of the reasons is Python has problems and That's absolutely fine. Nothing is problem-free, but we we need to have solutions for those problems and One of the problems that is very often mentioned with Python is the dynamic typing Especially Java and husk L programmers might not even try Python because they say a dynamic typing. That's Can't really work. Well My compiler checks against that And does everybody know what dynamic typing means or let's ask who? Who knows what dynamic typing means? I think that's almost everybody Yeah, I'm a bit short on time. So I'll I'll Continue on so what what is it that that Python programmers say afterwards? They say it's not really that bad So so who is right are the Python programmers just biased to see their own faults or are the Statically typed proponents just not seeing the light well in my opinion unit tests help alleviate the problem a lot because comprehensive unit test suites will help you Help you go through all all branches of your code and that's why they will execute all type errors They will execute all none none type exceptions and they will fail on it. I Have realized that myself that I am actually suffering from the dynamic typing I am producing a lot of these type errors in my code It's just that during the trying out and during the testing I find them very quickly and and they are not a problem for me anymore. So, okay The Haskell compiler and the Java compiler they they solve the problem systematically, but unit tests they do it They do it well enough for Python Another thing is the Haskell compiler does it for free Unit tests are not free because you have to write them, but then again you anyway want to write unit tests So they are a free site effect Another reason is are the great frameworks and at the moment that's especially nose and pie test There there are lots of talks about pie test, especially also here in Bilbao So I'm not going very deep into this but there's one thing that I want to mention as as the difference between J unit and pie test which I don't hear so very often so J unit is object-oriented which means that we have a base class with all with all kind of generic Assertions, and then we have lots and lots of test cases which have a setup and a teardown Very special assertions some helpers and then maybe lots of test cases So we an object-oriented programming We try to encapsulate many things into one class things that belong together They go together into one namespace the thing is that we for tests It doesn't really seem to work because we have on the one hand the functionality and on the other hand We have the state in which the thing is so for example if we have some some list implementation or some stack implementation We have different functionalities like getting the length appending something popping something And we want to have test cases for the setup when the the stack is empty when it's at Capacity or field So how do you organize these in an object either you you organize them? by functionality Then you can't use the setup Then you can't use the setup provided by the test framework each each test function has to do its own setup Or you you organize them by state which means that you you have to put widely different functionality into one object And where do the helpers go they the helpers must be somehow mixed in from the outside and mix ins work if you have Multiple inheritance, and if your language allows mix ins I was working in a project This is not from the project, but in that project we had to write a diagram in order to find out How much How to write the next test? Okay, I have to speed up a lot. PyTest is the answer. There is There there it has a more function based approach instead of the object based approach and that helps a lot Okay, the when not I'm sure that if you have some frustrating time with tests you would like to have a certificate like this I you want me to give you now a direct reason Which hopefully is exactly your reason and from now on you don't have to do any tests You don't have to put any work there, but it's not that easy the general General thing is that if it doesn't work for you improve it until it works But what I don't like about this is that it doesn't mention the costs And it's an economic factor. We have to see We have to measure how the the cost against the profit and maybe the profit is really high And maybe the costs are low But the costs are something that we have to be aware of and that we we have to mitigate We have to first find out where our costs first reuse them and only then we should try to improve Where do the costs come from they come from mismatches between TDD and other things the first mismatch might be you It's not common school practice to throw little children into the deep water because that's the way how they learn to swim fastest Unfortunately in corporate culture that is the case So you are a developer you start with TDD right now, and I think that's the the reason why Managers don't think that testing is a good thing. Why why they don't get that feeling Because it's simply not you can just turn it on and it starts laying golden eggs Well the the luckily we have we have a good argumentation here we agree that That having good tests is what we want and we have to write tests to have the good tests But writing them also gives us bad tests and now the good thing is that the more we write the more we learn to write better tests But now the problem with this is that learning is a human experience and you can't infinitely Learn you cannot learn too many things at once and you do not learn at all if the problem is too difficult And now we have a new concept the difficulty of testing I don't know of any metric, but I'll give you some examples of things that are very difficult to test So framework subclasses like jungle generic views, maybe, you know Where where the class really is a combination of class class Variables and mix ins those are really difficult to unit test Anything as an chronos is very difficult simulated environments like a mobile platform Where where where the platform on which you are developing is evolving and and you have to mock a lot abstract subclasses Evolve in in my case quite often before I really know what they are supposed to do. They become useful already I generalize without really having a good name for that thing that I'm producing there and that makes them really hard to test and Everything that has high interaction means a lot of mocking and a lot of mocking always need means a lot of complexity Another thing is that If you read a TDD book You get the impression that TDD is all that you ever need you just you just turn this one on and then everything works fine But what is not said there is that you need to have a process around it which fits with that so You need a specification gathering process, which is maybe in your company done by non-technical people and Especially you need an architecture around it and the architecture also needs to fit with TDD because if you do TDD alone Ignoring the architecture you will always build something Which breaks apart at iteration 3? The team has to be on your side because doing it alone just doesn't work and If you have a customer that you can educate in your process great But if you have an ever-changing customer, it's simply not worth it So so you have to to compromise between what the customer gives to you Maybe the customer gives you only very vague things Which simply doesn't work with TDD at all because TDD requires you to have very good specifications at the beginning Now I'm not saying that having good specifications at the beginning is a bad thing definitely not you want to have that But but if you don't have to you might have to compromise Then there are spikes I'm sometimes going off to little programming adventures and I'm and those that code might become really big But it's I'm starting really on an adventure without a plan not knowing where and then TDD is simply impossible so in the end I would say it's a Everything is in a relation you have to see is your competence good enough to to tackle the difficulty and Can you alleviate the uncertainty if if not if if you Encounter something which is simply too difficult for you to test and don't don't try to bite it until your teeth fall out maybe skip the tests on that one and and test something else first until your competence grows enough because It will always grow. No, nobody ever finishes. Nobody has this competence where he can test everything But you can also see that there are there three people on one side and the process is for hire So if you can get the process on your side There are fee for four people on your side and that should should give you the bias that a lot of testing is a good idea Okay, but how how to do it now? We agree on the why and we know that there are some costs, but how how to how to go to the next step how how to work with those costs one thing I This is mostly a recap actually because I have mentioned already the tools and it's very important Not only that you use the right tools, but that you learn them beforehand Don't don't start learning a completely new testing tool and a new testing etology in In the new project that you are building Which which will involve may involve maybe 30 developers what you want is you You build maybe a small experimental project on your own self for yourself Maybe a tic-tac-toe game and in that game you try out everything so you will learn all the dependencies and all the configuration of the testing tools Don't try to learn too many things at once Continuous integration helps you and and your team with with peer pressure because it shows you Everything It shows you whenever somebody breaks the tests the test don't help at all If you don't run them if you just write them and you don't run them regularly Continuous integration will not only run them, but will inform the whole team by email for example if somebody breaks it and you have to create a company culture in which nobody wants to be the one who broke the tests and with peer pressure you can It becomes fun Doing it the right way. I Also have realized just just do it just Just start with the first test if you have legacy code and you test after that's fine, too Just start with the first test and while you're doing the test you will realize. Oh, by the way, there's this one error case It's very easy to I just copy paste this test and change a little bit here and there and then you have the second test and And then you realize hey, well, it's just a few steps and I can have really thorough tests and I will have you'll have a comprehensive test suit in no time and Never stop the trying out I have mentioned at the beginning trying out is is a thing which gives you Which gives you a feeling of the code How how usable is it? How how performant is it? Does it make really any sense? I have just heard somebody tell me that he he has written these great tests and everything was working with tests But then when after a long time they actually started the application They realized that half of the buttons were invisible because they did have tests on that part and I Have mentioned these things which are very difficult to test especially with unit tests like interaction and asynchronous asynchronous connection to third parties and that the the point here is that functionality tests or integration or acceptance test whatever the name for that should be Will help you a lot and that gives you behavior-driven development as the the next step above There are the names are sometimes used differently, but also I have seen in this conference already talks which show you You have the TDD process as the small one using small unit tests And then you have the behavior-driven development cycle around that which cares for the bigger tests for the acceptance tests So there are cases in which I don't do any unit tests at all because the functional tests will catch it a unit That is also a thing right here. What is a unit? and I was very confused about this a lot because this name also implies somehow that everything is made up of units So everything can be unit tested and I disagree with that There are some things which are simply not meant to be unit tested You have to have tests on everything so that you know that the code runs through But sometimes functionality or integration tests are simply enough for it If you want to to create your your test test your test names then think think user story as A storage module. I want to access the file system by doing this and that and out of that user story You can generate the the test name and as I have now mentioned a few times Don't be dissatisfied with your progress Sometimes you see people that say oh, yeah, I'm doing I have a hundred percent coverage. I have three thousand lines of code for tests lines of tests and only 40 lines of code and you get the feeling Oh, maybe I'm not maybe I'm not satisfied with my testing Maybe I don't benefit because I'm not doing it extreme enough. I don't think that it is that that it is like that So don't overdo you will grow? Slowly and the thing is don't expect Testing to be the thing that you turn on and then everything works fine What do I mean with do it all wrong? I said already you should have this small You have this small Testing project you have a tic-tac-toe game for example and now try it It's it's only maybe one one file of code and you write comprehensive test suits suites That that have a hundred percent coverage coverage is a tool which I didn't have the time to to mention now And you will and then try out afterwards to introduce a bug that is not found by any of your tests and and that way You will find out it's actually quite easy to do and that way you realize how to write the tests You can create a monster test case for all features at once and then you try to refactor and you will find out that test is horrible You can if you test too many implementation details instead of the overall API your tests are really bad I have learned all of these things by doing them in huge production projects And I just wish I would have learned them in in very small projects So if this is all that you that you get from from my talk Try it out beforehand don't try to do too much learning at once and with that I I reached the end of my presentation and Do you have any questions? Thank you, Fabian. I'm very sorry, but we have no no time for questions because we are running out of time. Thank you