 O'r ydych chi'n ganddod o'r hoffaeth y tdd a'r hoffaeth tdd wedi teimlo. Mae'n hoffaeth yr hynny am y form. Mae'n hoffaeth eich tynnu negatifol, a mae'n hoffaeth i'r hoffaeth o'r hoffaeth. Yn y gallu'n gofarn i fynd i ddifwng. Roeddwn ni'n Rob. Roeddwn ni'n ganddodd. Mae'n hoffaeth o'r hoffaeth o 14 oed. As the slide suggests, I once appeared on CNN, not because I committed any crimes. I wrote an algorithm to analyse Twitter and in 2012 it got a little bit crazy, but it was a weird fun experience. I've been ahead of technology for two and a half years. I recently stopped that, very stressful job, went back to contracting and having a nice relaxing time. One of my interests is testing, and I also have a degree in history. So a bit of a mixed bag there. So we're going to talk about test-to-break and basically how test-to-break helps you build more robust, better software. So essentially, if I said to you, drive your car into a tree, you would look at me and think I was slightly mad. You think, who is this crazy guy asking me to drive my car into a tree? I will hurt myself. But the thing about madness sometimes is it can be a matter of perspective. And if you work for NCAP, which is the new car assessment programme, your job is to essentially crash cars into trees. Obviously they don't use trees, but they crash cars into walls and they crash things into cars. And the reason they're doing this is to see how safe cars are. They're essentially testing them in unusual circumstances to see what happens, because essentially they want to build or they want to make cars better, safer and more reliable. And when we write code, we need to do something similar. We need to ask difficult questions of our code to see what happens, to find bugs. So this talk is mainly going to be around the concept of unit testing. And just to cover off very quickly what a unit test is, if you're not fully aware, a unit test, and I think this is important actually because sometimes the term unit test can often be thrown around to cover any form of code test and it's not really necessarily accurate. So a unit test is a test for a unit of code. And a unit of code is basically the smallest amount of code you can write to achieve a certain piece of functionality. So what we can see here on the slide is something that I wrote for an open source library, it's just a simple get method that merges two arrays together and passes it back. You would write a couple of tests for this just to make sure that the, or a couple of unit tests, just to make sure you're getting back the correct array with the correct data in it. And that's essentially what we're doing here. We're not doing anything more complicated than that. And that's what unit tests really focus on. Things that get more complicated, they're sort of more code tests, integration tests, things like that. So there are three types of tests that you can do. And this is the first one. Anyone have any idea what type of test this is? Pardon? No, it might be. I mean, I've not heard monkey test before. Yes, exactly. The developer's favorite kind of test, no tests. I say that slightly sort of facetiously, but I, you know, in my career, and I'm sure some of you will feel the same, there are lots of places that basically don't write any tests. And I've been the person who's gone into companies and said, yes, you need to start doing some QA at least. So it happens. So, yeah, no tests. And I think there's a reason for this. I think there's a reason why this occurs sometimes. And that's because, you know, TDD and unit testing can be a scary topic to get started with, you know, because, and often developers are introduced to it in not in the best way always. Often it can be, oh, we've got some legacy code, we want to introduce testing, test the legacy code. And that doesn't really work. So there's a couple of tips that I would give to anyone who's sort of starting their journey on unit testing or test-driven development is, first of all, do test business logic, but don't test input-output, okay? And what that means is you're just testing logic, you're not testing things like databases or file systems, okay? Ideally you mock them, but even so you should still minimise your mocks if possible. Basically business logic, your code should be written so it doesn't care where data comes from or where it's going. So if you've got some data from the database and you process it using your business logic and you send it to the file system, like your code shouldn't care about that, that it's come from the database and it's going to the file system, or vice versa or whatever, you're testing with unit tests specifically business logic, okay, and that will help you. If you get lost in input-output, you'll struggle. The other thing is do test private methods and don't test big methods, okay? And this is the legacy problem. Often you'll get a legacy project with your 20, 30, 40-line methods and then someone will say test that. Now those methods are enormous and they have huge amounts of logic in it. There's no way you can write the test to cover all the paths through that logic. So a better approach is to break that legacy code down into smaller private methods. Now some people don't like using private methods in classes, but I think if you're dealing with legacy code and you want to get going with testing and you want to make your life easier, break large methods down into smaller chunks, smaller private methods, and you can use reflection to achieve that. So that helps. So if you're struggling with testing, doing these things will help you. So the second type of testing we can do is a thing called test to work, also known as positive testing, okay? And test to work is basically asking questions along the lines of does the system work as expected with standard inputs, okay? So you're saying in normal circumstances, my code should do x and it should do y. So an example of this from cars would be, a driver taking a valid key, sticking it in the ignition, turning it and the engine comes on, okay? That would be a test to work test and then a second one to that would be, given I have an invalid key and I put it in the ignition, the engine shouldn't start, okay? So this is a standard scenario with standard inputs, okay? That's test to work and you will do a lot of test to work. And test to work has its place and its use. It's particularly good for regression testing. The final type of testing, which is the one we're going to talk about today is test to break. And this has two purposes. The first is, can the system be broken? Can we break the system in some way? If I write a test that fires something odd at the methods, will something strange happen? Will something unexpected happen? And then the second part is, what happens to the system in unusual circumstances? Can I create scenarios that the system wasn't expecting? And essentially you're trying to, you do this because you want to find the odd bugs that you don't know about, okay? And examples of this would be, you know, instead of taking a valid key, I get a screwdriver, I stick the screwdriver in ignition, I wiggle it around a bit and see if the engine starts. If the engine starts, there's a serious problem with my ignition system, okay? You know, I built it to work with keys I hadn't thought about maybe screwdrivers or something else. And in the next part, and I think this is the really important one, would be the unusual circumstance. So as mentioned earlier, no one plans or aims to drive their car into a wall. But maybe we should check what happens if we do. So if we drove a car into a tree or a wall at 30 miles an hour, the outcome we'd want would be the seat belts tighten and the airbags deploy and hopefully we'd survive the crash. And this is like, you should see this a bit like failing gracefully. The car's failing, it's in a failure situation, it can't get out itself out of, but we're attempting to catch the exception, which is the wall, and handle that, you know? So that the driver and the passengers don't die, yeah? Airbags deploy, seat belts tighten, hopefully everyone survives. And this is the sort of test MCAT do to make sure that cars fail gracefully and that they can survive certain odd scenarios, because the likelihood of you, you know, I've been driving for a few years now and I've managed to not crash into any walls and my fiance would say I am a terrible driver, but you know, so it's unusual to happen even for bad drivers like me. So we're going to have a look at this in a code example and you should be able to see it all on the screens, I hope. But the first example we're going to look at is basically email validation and this is a slightly silly example, but I hope it will illustrate some of the issues. So if we look at our code and, you know, I'm not typing this, this is actually telekinesis, that's how smart I am, but basically if we get going, oh. Sadly not. And this is just started. Let's... Okay, yeah, it started, sorry. So essentially we have a very simple email class. We've declared strict types at the top of it. Everyone uses strict types now, don't they? Yeah? If you don't and you're using type hints, you're going to get type coercion problems, which is another bug that will drive you mad. And then we've written a simple test. We've written a simple test class and all we're doing is we're instantiating our class and seeing if we can instantiate it. And this follows the second law of TDD, which is you write enough of a test to cause a breakage in the code. And the first breakage you can cause is class instantiation. Bob Martin has a great video on this, okay? And what we're going to do is we're going to run this test quickly, fend a PHP unit and everything should go green. Yeah, so everything's gone green, we're okay, our first test runs. So we're now going to write a test to build a validate method because we want to validate an email address and we're going to look at how we write some tests to validate emails. So our first test is just going to be a test that validates a true email address. So a correct email address. So we're going to pass a correct email address in, we're going to instantiate our object and then we're going to call a validate method and we're going to do this assert true. One tip at this point with PHP unit is I know a lot of developers use assert equals, okay? Use assert same instead. Assert same checks value and type. Assert equals only checks value, okay? So you will get problems if you don't use assert same. But we're using assert true and we're going to run this test and it's going to fail and it's going to fail because we don't have a validate method, okay? And this is, again, back to TDD. We're writing a test to cause a failure and then we're going to fix our code to work with the test. So we go back to our email code and we're going to write a simple validate method. It's going to accept a string and it's going to return a boolean, okay? Now at this stage, we could write some logic but we're not going to. We're just going to return true, okay? Because that's going to fulfil the test and this is the third law of TDD which is that you only write enough production code to fulfil the test, okay? Now I know that this might seem mad but it does help to slow your coding right down and think through every logical piece. So we've run the test again and it works. Now we need to write a test to fulfil the test to work set. So we're going to write a test that passes in an email address that is invalid. We'll then write some more logic to make sure that our true test and our false test both pass, okay? And we're following a nice TDD flow here. So assert false and we're going to pass in email validates and rob.test, I think. We pass in. It's amazing when you already know what you've written. And we're then going to run this and we should get a failure because our method's going to return true and it's expecting false, okay? And now we go and update our code. So we're just going to do a little bit of regex here. Put up your hand if you love regex. Oh, you are awake, great, yeah, yeah. I do like regex after about 14 years of coding. I remember breaking a .NET system with my terrible regex, it just ran out of memory. So anyway, we're going to write a simple regex and all it does is check whether the string has an at symbol in it, okay? That's all it's going to do. And obviously, because we're using our disk clear strict types and preg match returns one or zero, I think, we're going to cast it to a Boolean just to make sure it all works. And then when we run this, it should work. Yay, it all works. And we've achieved test to work, right? We've written some tests and it works. Now at this point, we could say that I am an amazing developer because I've done everything I need to do. I've written some tests. I've then written some code that passes those tests. I've got 100% code coverage. Like it's time to go home. Like this unicorn developer can go home and have a glass of wine. You know, I can, I've done it all. It's all perfect, you know? And this is the thing. This is the line in testing that you get to. So at test to work, we've got to a line where we've kind of written some logic and we've written some tests to sort of show, yeah, it works. It will take a valid email address and it will take a bad email address. It will do some sort of assessment and it will say one's good, one's bad. But in this argument, you know, this example, this is silly, right? We know that this isn't going to work on email addresses, so we'd have to write some more code. So basically, we're now going to write a few more tests and we're going to write some tests to break. We're going to go, okay, we've got this logic, let's write some more tests and see if we can break this thing. So our first test, and a nice misspelling there of public, pubic. It's not as bad as pubic at least. And we're going to do test validate, false, obviously always, oh no, no TLD. So this one, what we're going to do is we're going to stick an at sign in the email address, but we're not going to give it a top level domain, yeah? Which will make it invalid, but it will actually pass our current logic, our current test. So this should fail. So assert false on rob at test, okay? Not a valid email address, okay? We're then going to write another test and it's going to be two acts, okay? So we're going to put two acts together in a otherwise valid email address and we're going to say this assert, after we've instantiated our object, we're going to do this assert false again and we're going to pass in our dodgy email address. So again, this will return true currently. And then our final test is actually going to be a bit of a strange one. We know that valid email addresses should have numbers in it, okay? So we're going to put a test there that's going to assert true to make sure the next layer of business logic we write will cope with numbers and will return true even if the email address has numbers in. So this time we're doing this assert true and we're going to pass a number in. Now, what we're doing here isn't pure TLD right now, but this is just a tutorial, this is just an example, but you know, you get a picture. So anyway, we get two failures because our top level domain and our double act emails don't work. So we're going to have to improve the logic slightly here. And we're going to write some more complex rejects. Magic copy and paste, it's brilliant. So this has like some checks in it so we can cope with numbers, letters, dots. We're only going to allow one act symbol and then we're going to have some more letters and dots, et cetera. And it will pass, okay? Now the point here is that what we did and I know we would have to write loads more tests to validate an email because emails are monstrously complicated. And we all hope that people like Taylor Otwell when they're building Laravel, they've covered this. They've tested out of their validate email method. Possibly maybe not, I don't know. But the point is, as we write more tests, our code becomes more robust. As we start going, asking difficult questions of our code, it becomes more robust. It can cope with more scenarios that users who will do any random nonsense will throw at our system, okay? And that's what we're trying to think of. How do we break this thing because we know that users are going to break it? And this brings us on to another concept, okay? Which is what is known as the working software myth. As developers, you'll have all worked at places and maybe, well, I certainly have, where people are pressurising you to get the thing working. Just get it working. But we also talk about this concept of working software together. Like it's part of the agile manifesto. And this isn't a critique of the agile approach or agile manifesto, but it's baked into our logic and our thinking as an industry. And we say we value working software over comprehensive documentation in agile manifesto. So it's there, it's baked in. We think about working software. But there's something not quite right with working software. It doesn't provide a complete answer of what we're trying to achieve. We want to achieve actually something more of than working software. So to highlight this, I'm going to look at the car industry and talk about some of their working examples. So in 1957, we have the Trevant, okay? And this was produced in East Germany. And it was made from a thing called duroplast, right? And they're now having problems with duroplast because it basically doesn't degrade. So they've come up with all sorts of ways to recycle it, but they're struggling. They're basically waiting until the sun consumes the earth to solve the duroplast problem. And the other great thing about the Trevant was you had to wait 13 years to get one. Imagine waiting 13 years for a car and a Trevant turns up. I mean, it's like, okay, say it was a Tesla, like people have been complaining like I'm waiting three months for my Tesla. Imagine waiting 13 years for your Trevant. We then have in 1970, we have the Lada from Russia, right? And this was built in association with Fiat, but they basically took a Fiat and turned it into a tank to cope with Russian roads and the fact that Russia had no infrastructure over under the Soviet Union to deal. They didn't have an AA or the RAC. No one was going to turn up and fix your Lada. It had to be simple enough that you could just fix it yourself. And then finally, my favorite, which is the height of British engineering, is 1973, the Royal Iron Robin. This loop, this is great. I think if you've ever seen the episode Top Gear where this is on, but the point here is, what's important to think about here is these are all working cars. Like these cars will get you from A to B. They all work, yeah? But would any of you buy them? I hope not. Maybe it's a sort of nostalgia thing, but also like would any of them pass NCAP tests? So NCAP was funnily enough introduced in the States in 1979 before, just after all these cars, when all these crack cars stopped getting produced. We got NCAP in Europe in 1997, thanks to the British Department of Transport. And you see the ratings now every year on cars, and it's part of the marketing gump that the car industry pumps out. We've got a four-star NCAP rating or a five-star, and it's worked in the other way. NCAP ran tests on the Land Rover once, and it only got one star. And Land Rover was so mortified this, but they just pulled the car. They stopped producing it. They'd spent years building this car, got a one star on NCAP, and they just scrapped it because it wasn't going to sell. They knew it wasn't going to sell. And the point is, yes, technology has improved so that our cars have got better, but testing the car industry has improved the quality of cars dramatically. And it's the same principle with coding. So working software isn't really what we're aiming for. What I feel that we're aiming for, really, is good software that functions well and is bug-free. Now, there's a slight caveat to bug-free. You're never going to remove all bugs from code, okay? That's basically impossible. But you should be minimising bugs as much as possible. I have a little game I like to play, which I call the two-minute rule. So if you sit me down in front of your website and I can't find a bug in two minutes, your site's probably okay. But if I go on to your website, open the console and find your logging, like hello world and this is debug code, I know that, most likely, that's just the tip of the iceberg, right? And you should think about this with your own code and your own software and your own projects, like if I just sat down in front of this or I've got a friend, a colleague, to sit down in front of this, how long it would it take them to find a bug, okay? And if it's within two minutes, there's probably some issues with your code. And also the functions well is important, right? If software doesn't function well, your users are going to have problems, okay? And maybe you're in a B2B situation where you're providing software to businesses, poorly functioning software is going to drive inefficiencies in those businesses, okay? And that's going to cost real money. Yeah, not like developer money. I know we get paid well, but like, there's very few of us, but we can cause a lot of damage with badly functioning code. Now, this is difficult to achieve. It's difficult to write good software. And the reason it's difficult is because we have a thing called the ignorance complexity problem, okay? So this is basically the reality that we as developers and just human beings live in. So we are ignorant and we have to work with complex things. So the chap on the left, we haven't got an up-to-date picture of him or a proper picture of him, chap on the left is Socrates, okay? And Socrates lived in Greece about 2,500 years ago. And Socrates came up with what's called the Socratic method. And you can see Socrates as the sort of first, what I would call or what you would describe as the first test-driven philosopher, okay? He came up with dialectic questioning, which is so baked into Western philosophy now that we don't even think about it. But it's a very simple concept. All Socrates would do to get to the truth, because that was his aim, that's what dialectics is about, search for the truth, is ask questions. Like, you know, and we see this elsewhere with the five whys and things like that. It's just asking questions of people until you get to the truth of an issue, okay? And that's what you're doing with test-driven development and test-to-break. You're asking questions. So there's a famous story about Socrates and basically his friend Chyrifon went to the Oracle at Delphi. They had oracles back then. We don't have oracles, only in the Matrix film, but we don't have them as a standard thing anymore. And Chyrifon went to this Oracle and asked, who is the wisest man in the world? And the Oracle responded, it's Socrates. And Chyrifon was perplexed by this and was like, okay, Socrates, fine. So he went back to Athens and spoke to Socrates and said, the Oracle at Delphi thinks you're the wisest man in all the world, basically in ancient Greece. And Socrates was, again, really, he wasn't sure about this. He was like, maybe, am I? So Socrates went and spoke to a lot of very, very clever, capable, wealthy people in Greece and asked them questions to work out whether he was the wisest. And he came back and they had a conversation with Chyrifon and Chyrifon said, so do you think you're the wisest man in the world? And Socrates said, yes, I think I am. Now that's arrogance. I mean, you think, God, this guy, yeah, okay. But his response is the important bit, what he came after that. He said, I'm probably the wisest because I understand how ignorant I am. Everyone else I spoke to claimed how great and wonderful and clever they were. Whereas I always ask questions because I don't know what the answer is. And what Socrates was getting at was a thing that's innate to human beings and, again, software developers, is that we are all ignorant, okay. And sadly, in this day of, or age of Twitter and social media, people love to throw ignorant, you are ignorant out as an insult of people. But it's not really because we all are ignorant. I went to the talk yesterday on MySQL and I've been doing SQL for at least a decade. But during that talk, I was just like, I know nothing about SQL. There's loads of stuff I don't know about SQL. I may as well give up now. Stop doing SQL, I don't know. I'll just pay someone to do SQL in the future. But the point is we're all ignorant and we can't all know everything, okay. And that's one side of the problem. The other side is complexity, okay. And the chap on the right is Friedrich Hayek, okay. And he's an economist. He's very famous in the 20th century, along with Keynes and Friedman. He's one of the most famous sort of capitalist economists of that century, won the Nobel Prize in Economics or something as well. And often Hayek is placed against Keynes as if they were somehow in a form of competition. But they're actually very similar economists. They were both trying to achieve the same end in the same period. They were both trying to defend capitalism in a period when you saw the rise of fascism and communism and alternate economic models. They were both arguing in different ways about how capitalism can work or be better. And Hayek's great insight relates to what's known as the economic calculation problem. And basically he argued that central planning would always fail because economies generate too much information to process easily. Like central planners will never ever be able to process the amount of information an economy can supply at any one time. So they will never be able to make well informed decisions. So we need a more nodal system where people work closer to the information which is what he described as the free market. Now what he was really getting at was basically because when you talk about an economy you're talking about people, individuals. He was basically making the simple points that everything in life is more complicated than we imagine. And it's the same in software development. We are dealing with every single day very complicated problems. So I mean we're at a point, we're at a difficult point as software developers where we're both ignorant and the things we're trying to solve are really complicated. Maybe we should all stop, okay? Maybe we should give up, what's the point? We're never going to write good software. But that's not true. There are ways of getting around this. And this is what Socrates I think got at which is basically ignorance is natural, complexity is a standard. We just have to accept that. That's just the way it is. That's the world and the universe we live in. But things like test to break help, okay? Because what we're doing with test to break is we're asking difficult questions of our code to uncover the unknown unknowns to borrow a phrase from Donald Rumsfeld. Great guy. So we ask difficult questions because we're not sure what answer might come out. But we've got to be looking for those odd answers. And if we do, we can capture the bugs before they hit production, which is at the point where everyone scratches their heads and the developers get shouted at. If you follow test to break, you can solve some of these complexity problems. Okay, so we're going to look at another example now, code example, to sort of highlight this point. So we're going to imagine we're in some sort of weird sort of steampunk-style world where Henry Ford's still all-powerful and he's still making the Model T. We're all driving around in Model T's, okay? But he's got a website. So we're basically going to build this website for Henry Ford and his Model T, okay? And it's going to be very simple. It's just going to be one website and one factory and some information is going to go in and a car is going to come out the other end. And there are going to be some rules around this production line. So cars can only be black. This is the great myth of the Model T. It's not actually true, but Henry Ford once commented on it and it's stuck. Everyone thinks you could only have a Model T in black. The cars can have two or four doors and the cars must have four wheels, unlike a reliant Robin. We're not going to, you know, you still see people trying to design three-wheeled cars. It's madness, but anyway, four wheels. So we've got a very simple set of code. We've got a car object. And this car object is going to accept our three parameters doors, our wheels, our color. And it's got some getters on it just to get the data back out. So we're going to instantiate a car and then we're going to get some data out. We've then got a order object and this is going to accept an instance of the car and based on that instance, it's going to run a order create method and just print out a simple message, okay? Order created for a car of color with doors and wheels, et cetera. Yeah? And then we've got some tests. So we've created some initial tests just to test that car object, get it up and going, you know, passing the right variables, make sure the getters work, cover it off the basics, it's all working, okay? And then we're going to do some tests for our order. So we pass the car in and we make sure we get the right message back. Yeah? Order created for a black car with four doors and four wheels and then there's one below for the two-door example, okay? So again, we've got to the point where we've written some tests and we've worked with code that is now working. And in our scenario, this should be fine. This is absolutely fine. You know, we've got one website with developers who know what they're doing because they're just their thing and we've got one factory and everything in our universe is fine. This will work, yeah? We have complete control over the universe. Nothing's going to go wrong. But the Model T was the second most successful car in history. And so Henry Ford realises how successful his car is and he starts going, okay, well, we're going to start producing this car in other parts of the world. So we're going to build more factories and we're going to start letting other people sell our car and they're going to integrate with our system and send us orders and then we'll make the car and then we'll ship it out to them, yeah? And then they'll pass them on to the consumer. So we're going to expand our manufacturing base enormously and then suddenly you have a whole load more complexity involved in your system because you have more people using it. And we all know what happens when we allow more developers to start playing with code, you know? People are going to accidentally or developers are going to accidentally send an order for a blue car with seven doors and nine wheels. That will happen. Now the problem comes is if with our code is at this point, our code will go, okay, yeah, I'll take an order for a blue car with seven doors and nine wheels. And then that order will hit the manufacturing floor and the people building the car will scratch their heads and go, you know, they'll look at the enormous paint rack of paint, all those cans marked black and they'll walk up and down it for about half an hour going, has anyone seen the blue paint? And, you know, it will lead to huge inefficiencies and slow down. And then it will get to the end of the line where they start fitting the wheels and they've lined up seven and, oh, there's only four spaces for the wheels. What do we do? Okay, and you've let huge amounts of inefficiency into your system. Now this could be easily solved if we'd applied a bit of test to break to this scenario in the first place at no real extra cost. So how do we improve this code to make sure it's better? Well, our aim is to make sure this code can only be used in one way. If it can only be used in one way it can't break. So we're going to write some more tests. And we're going to use in PHP unit a thing called annotations. If you haven't seen annotations before they allow you to do things like check for exception messages or checks for the actual message in the exception and various other things. But we're going to write a test based around purely expecting our methods given the scenario to throw an exception. Okay, it's going to say no, you cannot do this. So our first test says it expects an exception and that it's going to get a message of invalid car could not create order. And we're going to write a test for test create three doors. So we're going to try and create a three door car. Okay, so we can see that in the code we're passing three doors rather than four or two. And all we have to do is call order create. We don't have to do any assertions because the assertions are in the exception annotation. And when we run this, it will fail because there is no exception thrown, yeah? So currently we know that this code is just going to work, it's going to be fine. It's going to place an order for a three door car. So we're going to write a couple more tests as well to solve the other problems around wheels and colour, yeah? So we're going to create two more exception situations. One for five wheels because we only want four. And one for the colour blue, okay? And we're going to make a little amendment to our code, just, yep, two doors. Two doors and we're going to pass and colour blue. And then when we run this, we're going to get three failures because none of them throw exceptions. They all accept the input, okay? So now we're going to improve our code. And all we're going to do is we're going to wrap our simple order statement, which just runs every time, in a little if statement. Now, obviously you might abstract this code. This is very hacky code just to show you what you do, okay? Don't just, like, concatenate ands or ors all the time in if statements, it just makes them look messy and complicated. But we're going to throw our exception if we can't return. So throw new exception. And we're going to pass in our message, which comes from our test, pass that in, and then we're going to create the if logic. And in a moment, you'll see that I realise that I'm not checking the colour. So this is another little tip with in array in PHP. In array takes a third parameter of true or false. In PHP's lovely way of, like, not checking types. If you pass in true as the third parameter, it will check not only the value but the, that it's an integer. So if my array had strings of three and four, it would fail, yeah? So little tip on in array, pass in the true parameter. And I've just fixed the code so it's getDoors and now we're creating getToWheels and getColour and then we'll put the checks in place using our three equal signs to make sure we get it just right. OK, and then we save that and then we're going to run the test again. And when we run the test again, it all goes green. Now, at this stage, we've just made our code much more robust. It will only work in one way. If it tries to do anything else, it will fail. Now, this might seem a waste of time, but if you think about it, it's much cheaper to go in later and fix the logic on this code and go, OK, now we'll accept blue-coloured cars and three-wheeled cars than it is to allow this code to fail and pass something on somewhere else, which is actually important and has costly side effects, OK? If you allow inefficiencies to occur in your business because of sloppy written code, that's going to cost you a lot of money and it's going to be a lot more money than it takes a developer the 20 minutes or half a day to go in and change the logic of this code and for you to do a deployment, OK? So, test-to-break, again, helps us make software more robust. Once you've written some tests, you think, OK, what could I do to break this system or challenge this system? And you write some tests and you will make your code more robust so that when it hits production, you have less headaches. So, to summarise, basically, as we stated, working software is not necessarily good enough. We can do better, but writing software is hard. We are ignorant, sadly, and we're dealing with complicated problems. So, always write some tests. Stress your business logic, see if you can break it. And if you do that, you will write code that will only work in one way, but it will always work. Essentially, you will have crashed your car into the tree, everything will be good, and you will be happy, smiley face. That's me. Thank you. I'd just like to open up the floor to questions now. Have you thought of using that scene from sideways where he tries to run the car into the tree and he misses? That might bring us on to the topic of mutation testing. So, to test our test work, so if we write a test where we miss the tree, then, yeah, absolutely. So, there's a tool called PHP Infection. I've not actually seen that film, by the way, but there is a tool called PHP Infection that does mutation testing in PHP now. Quite a complicated topic. You could do a talk on that, but that might be useful as well. That helps. When you're testing the file, and if that test fails, that's when he misses the tree. Yeah. Any other questions? You said, like, don't worry about the input and output about the data sets, right? Does that gain the confidence for the developer? Say that again? While testing, don't worry about the input and output of the data sets, right? Because, like, we might be interacting with the database and all, right? So, does that gain the confidence for the developer? So, the point there was that was very specific to unit testing, OK? So, if you're doing unit testing, you're testing your business logic, you're not worrying about databases. When I first started testing, I made the mistake of testing databases with unit tests, and all that happened, even though I was using SQLite, all that happened over time is the test just became slower and slower and slower, and that makes things way less efficient. You can test databases and what have you and how the code interacts with databases using other types of tests. So, your sort of end-to-end acceptance tests may be doing Cucumber or something like that with Selenium and your browser testing, and it's about, like, when you do that as well. So, you should be running your unit tests, like, all the time, like checking unit tests all the time. But to do that, they need to run in seconds, like, half a second, a couple of seconds, OK? Not, like, minutes. So, that's really why I said, keep away from... One of the reasons I said, keep away from input-output is that your unit tests are just for your business logic, just for the things, the decision-making parts of your code. That can be difficult. Like, I've been recently testing WordPress with unit tests, and WordPress is so linked into the whole Mplus 1 philosophy that it's almost impossible, but it can be done. But that's all I was really talking about. I wasn't saying don't test, like, your integration with other systems. I was just saying that for unit tests, it's just specifically for business logic. All right, thank you. Anybody else? Any other questions? How would you explain to someone who's not technical, but who's got a lot of influence in controlling a budget for a specific project that we should write unit tests, and unit tests are very important for good software? Obviously, I'm a big fan of getting a stick and beating people with it, but that doesn't tend to go down well with management. I'm joking, by the way. I think with all of this stuff, and I've been a manager, I was a manager for two and a half years, the approach you have to take is you have to take something that's very technical and important and put it in the language that non-technical people will understand, particularly commercial people, and the whole point is the theory of the full cost, right? So the full cost theory is very important, okay? To do some development and get that development working and bug-free will have an associated full cost. And what happens often is the choice of whether that cost is taken in pre-release or post-release, yeah? And loads of companies, you see this, they do a bit of work and then they spend years afterwards fixing the rubbish code they built, okay? And they may never actually hit the point of achieving full cost. So your main argument for testing is, and this is where it's key, is that costs, if bugs get into production, costs begin to spiral. And they will also be untracked, okay? They will lead to inefficiencies in other teams. So you have to explain the importance. The reason you test and the reason you take a little bit more time with your code is to get as much of the cost in and done before you release. Because if you don't, costs afterwards will just skyrocket. And you see it in companies all the time. Developers become really inefficient and all they're doing is fixing bugs. They're not actually doing useful stuff. So that's the way you explain it. You take it into financial terms and you say this will cost you more money if you don't do it than it will to just do it. If that makes sense. I think there was a question at the back there. I was wondering where you came on tailored exceptions. So in your example, you had one exception that covered different use cases of failure. A thing I've heard go back and forth a lot is whether or not you should have exception, bad colour exception, too many wheels exception, not enough wheels. I'm interested where you find the balance works for you. Yeah, I think it all depends on what you're building and that gets more complicated whether you should have a specific exception objects for certain failure scenarios. So I think it's useful, particularly with the annotations, is you can check message and you can check code. That can be useful for differentiating exceptions. Obviously in my example, I was just trying to get through the code as quickly as possible, get it working. But I think tailored exceptions are useful. As much information you can pass back up the chain to say, this is what went wrong, it's always going to be useful. Whether you need to have a specific exception object type for that or whether you just go with a global one, I think depends on the size of your application. So I've written a small open source library and I just changed the message in a global exception object and changed the code. That's all I do, but it just depends. Cool. Thank you. Any other questions? They started testing cars when they found out when some drivers crash into trees and end up dead. So do you have any tips because we don't want to wait for that to happen before writing the tests so to prevent failing production applications? Oh right, so you mean like even before you write tests, how do you... No, how do you decide what you're going to be testing? Right, okay. How far do you go? Yeah, so there are useful approaches to this. I mean, ideally one approach might be that you do some initial testing. So TDD works up to a point, right? So the idea that you write your test and then write your code works up to a point. But I see it as like, yes, you'd use that model to begin with, mainly to achieve the test-to-work thing I talked about. Test-to-break is usually something that comes afterwards. So you basically use test-to-work to make some logic work and get a rough sort of sketch and then you come in with test-to-break to make it much more robust. So that could be done in a number of ways. You can do that yourself. You could work with a tester, a key way to come up with some failures, some scenarios, or you could just hand the code over to another developer to write some tests to see if they can break it, if that makes sense. I think there's a number of approaches that you can use and you're not obviously... You're not going to get to 100% perfection, but your aim should be to get your code to a point where it's just edge cases. Are there tools or something in the community because we are with a couple of developers here who all have been in certain situations, I might have not been in. So are there any... So I would say it's part of a whole array of tools that you deploy to build robust software. So testing is one approach. So obviously your PHP unit, you can use that. There's Behat, but are there useful tools to catch bugs? Well, I was not talking about testing tools, but more about how do you decide what you have to test. You talked about testing an email address, and we now all rely on the framework developers to test that. Okay, so a basic rule in that scenario is you don't test the framework. You test your business logic, you test your bit, and then you might do integration testing to see how the whole thing works as a whole, doing automated testing with tools like Selenium. But for unit testing, again, it's just the business logic that you write. So if you have to assume, if you're using something like Laravel, that Taylor Otwell has got it right. He probably has, but you have to assume, and all the guys at WordPress have got it right. You can't go into WordPress and start testing that. You just have to sort of work with it. That's not to say you can't write some scenarios to sort of test the integration slightly, but yeah, it's focusing on what you're writing as best as you can. So I think his question is in the car example, you knew that maybe somebody once would come along and say a blue car or five wheels. I think the question is how do you come up with the imagination to decide what might break? Okay, so I think that's gonna be a bit play it by ear. You're gonna have to come up with that. So one way is what you do pre any writing of code. So this can be in the planning stage. So sitting down with a tester and sitting down with a tester and a tester going, okay, so these are scenarios I would think about. Maybe doing an example mapping session where you go through the rules and requirements of the system and you come up with actual concrete examples, their approaches you can take. You're not gonna get this perfectly right, but I think, and there's the hallway testing which is what I was trying to touch on with the idea of letting another developer write tests for your code is the idea of if you let someone who's not seen this thing before have a go at it, they might see the things you've missed.