 All right, and I, am I okay? Yeah. Great, okay. I just have one slide about coding by intent, but it's so related to test-driven development that I thought I would include that one slide. So most of the presentation is going to be about test-driven development. My contact information is here. I'll also have it at the end. And also I have some postcard size flyers for other resources, as well as a link to this presentation. And so the slides are available at that URL. So I looked up the definition of test-driven development. I use Google a lot when preparing for presentations and I didn't come up with any precise definition of it. So I kind of merged a bunch and added some words. So it's a software development approach where independent automated unit tests are written before the code. So some key words here, independent. So you got test A and B. Test B should not rely on test A, automated. And then unit tests. And this one gets a little bit, the unit tests I'll talk about more. I kind of fudged those rules before and I think a little bit. And I think if you get into test-driven development, you might start fudging it and do things that are a little bit bigger than a unit or that some people might say, well, that's not a unit because it stores information in the database or whatever. So the key mnemonic, if you know, if you've done test-driven development at all, you've heard of red, green refactors. So these are the main stages or actually I should say the main steps of test-driven development. And the reason why I didn't say stages is because typically you wanna do very discreet tests and so this all happens very quickly in a typical environment, 15 minutes, maybe 30 minutes for each test. So you start out by writing a test before you've written any line of code and that test automatically is going to fail. So anyone wanna shout out why the test will fail? Okay, I can't quite hear but I think the answer is because, yes, there's no implementation. Yes. And that counts as a failure. So then you write a code to make the test pass and in the simplest possible code that you can write, I've had, I organize a user group where we do test-driven development and people are new to it say, you want me to hard-code a return value which is like a development no-no. And the answer can be yes and I'll show you an example of that. So your code passes the test grade so you've proven that your test works as one of the main purposes of the green phase. And then the next phase is refactor. I don't quite like that color gray but I picked it up off the internet with a reference there to where I got it from. So there you take a look at your code and you say, okay, how can I improve it? And I'll talk about different ways of improving code but it might be, like kind of one example is, let's say that you've got a statement if breed equals x, else if breed equals y, else if breed equals z, else if breed equals q and you start starting to notice some repetition. So that's a sign where maybe, okay, maybe it's time to refactor. And a key with refactoring is and this can be fudged a little bit but you don't want to change existing functionality or make an enhancement. Now, sometimes when you refactor, it naturally will handle test cases that you haven't written yet but basically you don't want to make big changes in behavior. You're just trying to make the code work as it did before, so that's that. So to kind of illustrate this a little bit better, this is kind of a classic example that's used when introducing people to test driven development is the fizzbuzz example. So how many people here are familiar with the fizzbuzz game? Okay, so a good number of people aren't. So in fizzbuzz, it's a party game. So you form a circle, first person says one and the next player says the next number which is two, except if it's divisible by three, you say fizz and if it's divisible by five, you say buzz. And so the requirement in this exercise would be to create a function that returns fizzbuzz or fizzbuzz, fizzbuzz or fizzbuzz based on these rules and actually that's not a very good requirement because I realize here that I also otherwise I'm create a function, oh no, I do it. This is that returns a number. So let me talk about using this, let me talk about the red for a minute. So for a new system, as we said before, the first test fails because no code has been written. So here, this could be the first test that you write, you say assert equals fizzbuzz dot value of one equals one. And so this is JUnitSyntax and then you, so I'm not gonna go to the green because I wanna talk about red, so let's say that you get that test to pass and maybe you either just return one, you hard code it or you return value, either one. So then you write your next test which is value of three and you expect to get back fizz. And so in this case, you're not failing because the code doesn't exist but you're failing because the wrong value is being returned back from the function. Now in the green step, you write the simplest code to get it to pass. Point is basically to verify the test and to make it quick so you don't wanna overthink this stage. And then again, I'm using a very simple example. So let's say that you already satisfied one, three and six and that for the six, you put in some code which says if value is three or value equals six, return fizz. And so you can see here there's some duplication. So this is the example of fizz, maybe I don't know the modular function, maybe I don't think of it. So just write this out because this is the quickest thing to do. So then we come to the refactor phase. So in the refactor phase, you improve the code without changing functionality. And here's where it gets a little bit. It's not 100% true, but so here I've got this duplicate value, value equals three or value equals six. That's some duplication because I'm saying value equals twice. And for those of you know Java, I already noticed there's a syntax error, should be equals equals, but anyway. And imagine you had, you know, you tested for nine also and you said or nine. I mean, then you would start to see the duplication even more. So here you notice the duplication. You say, okay, let me try to get rid of that duplication. And you say, okay, well, they're both divisible by three. So let me just change that to, if the remainder of value divided by three is zero, return fizz, else return value, you run all your tests again, they all pass and you refactored it. And because you have an extensive set of tests, everything passes. I'm sorry, you can be confident in your refactoring. So test-driven development kind of works hand in hand with coding by intent. So if we come back to the tests, fizzbuzz.valueOf, so I, this is saying, I'm intending to have a function called valueOf, or I'm intending to have a function that evaluates a number and gives me back a value. So you don't write the function first. So here's a slightly more complicated example of coding by intent. So let's say that I, coding for scoring a bowling game. So I could create a games class and then create frames within that and make sure I've got all my getters and setters and everything like that. But instead, what I want at the end is I just, I write what I want, I think about the core logic. I want to say, okay, for every frame in a game, I want to add up the score of all the different frames to get the total score. And then you can use your IDE, particularly if you're using something like IntelliJ, VS Code less so. So with IntelliJ, I haven't tried it with VS Code or Visual Studio or anything like that. I can just click on these different things and it says, well, game.frames doesn't exist. You want me to create a class called game and you can say yes and it'll automatically generate the code for you as well as the getters and setters. So the idea is you start with your core logic and then have it create all the supporting logic for you. And even if you don't have something that generates it for you, it's still kind of useful to start this way. So here are four principles of good software design from Kent Beck and he was kind of the person who coined the term test-driven development and is well-respected in the software crafting world. So, and these are in order priorities. So the first most important thing about your software is that it passes your tests. And so if you're following test-driven development, you've got tests that execute all of your code. The next is that it reveals intention so that it's expressive, clear and understandable. And except for documenting the variables and return values of an API in general, you don't want to have comments in your code. That's considered a bad practice. So your code should be obvious enough that by the names of your functions and methods and the structure that you've got that you can follow the code without needing comments. Then these last two can be in terms of priority, can be reversed. No duplication is the easier one to understand. So you've got code that does the same thing in multiple places. It's better to reduce that duplication. And then fewest elements and what Kent Beck means by this, I've seen it expressed in different words but I think of it as no unused code. So with agile development, this is more of an issue. This was a bigger issue in legacy code days but it can still be an issue where you're saying, well, maybe they're gonna wanna have this in the future. So let me code this for something that might happen. And then you get this big complicated thing to handle something happen and it slows you down and there's a lot more code to read. So you want your code to be as terse as possible. Terse not in terms of, I don't mean in terms of being cryptic but you want it to be as compact as possible. There are all these other solid principles which also relate to this but I like this because this kind of covers what solid tries to get at. So if you've got something that, for those of you who are object oriented, if you've got something with multiple responsibilities, well, that's making it hard to reveal the intention of that object. So there are some advocates of test-driven development that say this is the only way that you should develop. I'm kind of trying to find the balance with that so I don't necessarily agree with that but it's definitely a very powerful way of coding. So first of all, and when I first started, I was thinking, well, that's great because you know that everything is tested but that's not the real power of test-driven development. First of all, it encourages a simple modular design because you're writing something that right away is testable so you have to understand the input values and what you expect to get back. And another really cool feature of it is you've got self-documenting functionality. So if I put in these values, this is what I expect to get back or this function should do this and you've got it recorded rather than in some specs. So if your test cases are written well, this can be very useful. The biggest thing, but this kind of gets more into why it's great to have something that's well tested is you can refactor with more confidence as kind of understating it. If you feel that you've got good code coverage, you can do pretty large refactorings with a lot of confidence. And I'll talk about where that refactoring maybe is you shouldn't have so much confidence. Find defects earlier, good code coverage, faster in the medium to long-term is not faster in the short-term. Like I'm doing online coding quizzes and if I did test-driven development or I need to develop something in half a day and I know it's gonna be thrown out because it's a test, I wouldn't necessarily do test-driven development. So it requires upfront investment. Complex systems will require mocking. I'll talk about that. If you think ahead, and this is kind of an issue with that some people have with Agile, if you think ahead more rather than just the next two weeks, you might make better decisions. But it's kind of proven that it's better to make that trade-off. But if you can spend a little bit of time thinking about, okay, what are some requirements that are coming up? Maybe there's something I can do a little bit differently now. The tests that you write require care, especially if your systems are undergoing a lot of requirement change. And you might spend too much time on trivial test cases. All right, so let me talk about mocking for a minute. I think this will work. Actually, let me ask, who here is familiar with the term in information technology, not in when you're with your friends, mocking? Oh good, that actually seems like that's kind of a, it looks like that's bigger than how many people said test-driven development. So let me just explain it for those of you who didn't write it. So a mock object, and again, I had a hard time finding a precise definition of it. So a mock object is an object, and I wanna say or method. I mean, a method is actually a type of object you can think of, but an object or method that is used to simulate behavior of another object. And it is used usually to isolate behavior and speed up execution. So let's say that you have A calls B, and you wanna test A, well, if you write a test for A and it fails, you don't know if it, maybe it's because of B or C, or maybe it's really hard to create a scenario where the B function returns a specific set of values. So what happens if B returns negative? So in the mock, you would write a substitute function and there's all sorts of tools that can help you dig in and substitute this substitute in here. So an example down here at the bottom, let's say that you wrote a get JSON function that retrieves complex JSON from a web service. And here's an error here that should obviously be one line. And you wanna be able to mock a specific JSON being retrieved from that function. And you're doing, let's say that B is the web server that you, where you're calling to get the JSON. So you can mock the return results so that you just, instead of actually going out and calling the web server, you actually just return some predefined JSON. And wouldn't necessarily have to be predefined. You could make that function as complex as you want. You could say, okay, well, if it sends me these type of values, return this JSON, otherwise do this. And so this has, in this case, the main advantage of it is performance because if you're executing, say a few hundred test cases and you have to go against the API, that might take time. But it also has the advantage that you don't need to worry about bugs over here or if something doesn't work, you don't have to worry about if it's because of over here. You can say, I'm presuming I'm gonna get JSON in this format. Okay, let me just quickly check how I'm doing on time. 216, okay, great, I'm doing fine. Whoops. Okay, so some challenges when you get into test driven development. I mentioned earlier, rapid prototyping that will not be saved. Algorithmic, so I wanna do something that's as fast as possible. So that's you want to improve something. You don't want something just to work and return the right results. You want it to return it as fast as possible. I had an interesting conversation today with somebody at the break and if anybody has any insights into this, this is great, but data analysis, that's also another challenging one because you're analyzing all this data. Are you analyzing it in the right way? Not does it return a number, right? Is there some factor you're not thinking of? And then, if you're calling to the database or the API, that requires more work. And then obviously you need the support, you need to know how to do it, you don't necessarily need management support if they're not over your neck enough and they say, okay, you've got two months to do this. You can try test driven development even if they don't. Support it as long as you think you can get the work done in the same amount of time. Okay, so test driven development is not the whole story. So there's, if you look up testing pyramid, there are lots of people who've written about the testing pyramid and it just means that there's different levels of testing that take different amounts of effort. So the reason that unit tests are so good is because they're low cost to develop and they're fast to execute. And then at the very top level, you've got the UI tests and there's all sorts of reasons that those are difficult and fragile and there are applications that make it better but I've never seen an application. It's always very tricky because if you change a little something in the user interface it can often ruin all of your UI tests so those are slow and they're high cost to develop. And then you've got this middle layer and there's all sorts of different strategies for the middle layer. Some companies will employ multiple of these strategies. Some will only do one. Some rely a little bit too much on just the unit tests and so you've got acceptance tests. You've got something called acceptance test driven development. So that one's kind of interesting. So with acceptance test driven development you're thinking of kind of like a user scenario and so you write an acceptance test. If I submit this bill I expect to be able to pay it something like that. I'm more familiar with unit tests on the bottom and then you can write out 20 of these and they actually execute code and they'll fail because you haven't implemented them and so you can use acceptance test driven development to then drive test driven development. So you can take one of those and start writing the test driven, the tests that will help that one succeed and eventually that one once you've implemented everything that you need will turn green. Behavioral driven development is kind of similar. You've got end to end testing, API testing, service testing, et cetera. Okay, so any questions about that? Okay, so this actually went a lot faster than I expected it to when I mocked this off but this is the last slide and then I'll take questions. So if you haven't tried TDD, I really recommend that you try it. So you can read more about it online. If you wanna ask me questions about it during the question period, I can talk a little bit more about how you get started with it. The big advantage to it is that if you follow it, you're gonna create highly, high quality, maintainable, extensible. And I'd like to add into their anti-factorable code. And then I use a group called Boston Software Crafters. It's on Meetup. Oh yeah, I've got, as I said before, I've got some postcards back there with information about the Boston Software Crafters and about myself. So you can check that out. I'd love to see you guys there. Here's my contact info and resources. So this is my email if you have questions. My website, my LinkedIn profile. I'd love it if you connected to me on LinkedIn. This is the Meetup that I was talking about. And then I also run, I have a website for that Meetup. Right now it says WordPress. Actually I think that's wrong. I think it's bostonsoftwarecrafters.wordpress.com. Also just a pitch for myself. I've decided to go back to work instead of consulting. So I'm looking for software engineering job opportunities particularly at an agile environment where I can use test driven development. Looking to collaborate with others for open source projects and I'm available for onsite training. And all the slides are available again at bit.ly-slash-ethan-tdd. And if you've forgotten your pen, again I've got the postcard flyers at the back. So I can open it up to questions. Yes, wait for the microphone. Hi, yeah, so I try to use TDD in development and I find that I can really get it to work for simple use cases where I have a bug. I know exactly what's going on, the end result. I write the test, it fails, red, green, refactor, that kind of thing. But the part that I struggle with in the real world is when it's a more complex feature, sometimes I don't always know what the implementation is gonna look like. Tell me where it's, what's that again? I don't know what the implementation is gonna look like until I start actually writing it. So I don't know what test to write until I start writing the code, but it kind of breaks the TDD principle. We're supposed to write the test first. Do you have any insight into that? Well, I mean, as long as you're not, this is where I'm at a true TDD adherence. So lots of times I do that, I'm like, okay. And so maybe you write a little bit of code. I think the challenge is not to get too far down. So, test driven development is also called test first development. So I think I'm gonna coin a new phrase, test soon after development, but that would be T-sad, so that wouldn't be very good. But yeah, I mean, I find it hard to adhere to test driven and also if you're like learning some new technology and you don't even know how the API works. And so, I mean, yeah. Again, the people who really preach TDD or nothing, I wouldn't want them to hear me say that. But I think, does that answer your question? Yeah, so maybe there's some flexibility in that you can probe around a little bit and then write some tests and then write out a full implementation. And I sort of find that when I'm cheating, that I've still got the test in the back of my mind. So I think the test driven development kind of mindset kind of helps me when I'm not truly writing the test first because I'm not sure on the syntax. But I still got the mind, I'm trying to find something that once I write it, I can make a quick test for it. Gotcha, yeah, that makes sense. Yeah, thank you. Other questions? So as someone who's working on a, as part of a contributor to a large scale open source. Can you hold it a little closer? Yeah, so as someone who's working as a contributor to a large scale open source project, what I found is someone who's been writing tests for a non-test driven development approach. It's like, ideally, and I think many other people on part of the team would want to go towards test driven development, but we started the project just by developing for a while. So it's kind of like playing catch up. And one thing beyond just writing tests for already existing code is trying to push towards a more, like when the community makes like a pull request or something, how can we incentivize people to provide tests with their code as well? Because it's kind of like when you get someone who wants to contribute, it might come across as somewhat like if you ask a... So I think I get the question. So you're saying, okay, you're part of the open source community. So are you a moderator or you're just a contributor? Just a contributor. Okay. So I mean, so really it's kind of up to the moderators to encourage that. And so I'm not a moderator myself, is anybody here an open source moderator? Okay. So I think you can, when you're looking at the pull request, if it doesn't have tests that test what you do, I mean, I would, again, since I'm not a moderator, maybe I'm stepping into hot water, but I say, okay, this is great. It looks like the code works, but where are your tests? So that's not quite encouraging test-driven development, but at least it's encouraging testing. So I don't know, can you speak, do you mind speaking to that? Do you have an idea on how to do that? Yeah, we often, I'm part of the foreman community and I work on an open source project called Catello and one of the maintainers of it. And we often will ask people, please, can you please add tests for this? Okay. And then long-term contributors, expect that now they're thinking about the test before they even start implementing something and maybe using TDD. So you can kind of enforce that in your community. And is the open source project that you're working on, is it kind of written with test-driven development or is it kind of? I wouldn't say strictly, but I think we have the mindset that you want to, the code should be tested, the majority of the code should be tested. And part of it, I do think if you're checking that there's tests every time you check in, usually check-ins are small pieces of work. So maybe that's kind of encourages at least the T-SAT approach that I was talking about either. Yeah. Yes. Hi, Ethan. So what about, how can we use... Hi, Ethan. Sorry. Sorry. So what about the legacy codes? Legacy code, did you say? Yes. So legacy code is a lot trickier. And so it depends on the extent of the change that you're making. But what you want to do is, okay, I got this huge, ugly chunk of legacy code and I need to add this piece of functionality. Is there a way that I can safely untangle the threads of that piece? Sorry, I'm trying to use a spaghetti analogy. Is there a way that I can isolate that functionality so that it is more modular? Oh yeah, that's something else I wanted to say about the previous slide. Is there a way that I can isolate it so that the piece that I'm modifying is more modular? So it might be that you've got 1,000 lines of code and maybe there's 100 lines in the middle that you want to change how it behaves. So maybe you put that into a function and then where the legacy code originally was executing that, you call the function. And so you're in a little bit danger of regression but if you do it carefully, and you might not have tests that execute that, but if you can figure out how to isolate it, put that one piece in a sub-function and then you can start writing automated tests for that. That's one piece about legacy code. The other piece with legacy code is there's something called golden master testing or, I forget the other term for it right now. But the idea with that is, and again, you have to have a system at least. I mean, some legacy systems, there's like no entry with APIs at all. But presuming that you've got entries that it's API based at least. The idea with this type of testing is you create tests that test all, like tons of combinations of values and then you record what the results are of those values and you save that and you call that the golden master. And you can, I've even seen this done even with like screenshots, that's kind of interesting, but it's more like your tests create a text output and then you make some changes and you run it again and you see if you get the same test output and then maybe you find out, well, this value really should be seven or maybe you add some new features. So you can then say, okay, I've changed this. These are the changes to the golden master, am I okay with those? And then you make that the new golden master. Other questions? Do we have a question from this side of the room? You sure? I'm one up here. I don't know why this went dark. Hello, it's a nice talk. I don't have a question exactly, but I have a remark to make based on the earlier question of incentivizing the TDD in open source communities. I think a great idea would be to have badges, like Fedora community has badges. What sorry? Badge and imaginary badge. So one thing it should, let's say it's a shield like looking icon with TDD champion written on it, right? It does not need to be in physical batch. So one thing I can tell you is that developers are suckers for a good batch. So they will eventually fight for it. Yeah. Actually, thank you for that. And I also wanted to mention here on this, this was more about the legacy. So this is a great thing to strive for. So you're spending most of your time on the cheap things. If you've got a system that's not well architected that where you've got various dependencies between the different objects, things that are intertwined and stuff like this, this is a more dangerous strategy because it's gonna be, it may be that it's in a lot of the interactions between the different things. So you might need to spend more of your time here or maybe you don't even have APIs and maybe you need to spend more of your time up here. So this is, this strategy kind of presumes that you've got a well architected system without a lot of surprises. And if it was perfectly architected, you could probably do almost all of this and just a tiny bit of this and a tiny bit of that. But I think that that's rare. Okay, let's see how much time do I have? Five minutes, any other questions? All right, great. Give you five minutes back and again, my contact information and information about that user group is on the back table. So thanks a lot everybody, it was great. Good questions.