 Just to get a sense of who's in the room, how about, thank you, how about stand up if at your company on your projects, if you're using test automation, okay, so that's almost everybody. Now, wait, stay standing for a minute, okay, now if the longest project you've been using test automation with is less than six months, then sit down, so if you've been using test automation on a project up to a year, then sit down, okay, up to two years. So what's the longest you've been running a project with test automation, three years, eight years, wow, excellent. So as you've run these, you can sit down now, thank you. As you've run these projects for six, eight years, the nature of the tests and the way that you organize them have changed over that time, correct? I don't think you would still be running test automation if you hadn't. I've run into too many organizations where, you know, they jump into test automation and then six months or a year, they're having a hard time maintaining those tests, they're having a hard time keeping things running, they don't know what's where, when something breaks they're not sure what's going on, and so they end up, well usually it happens slowly, they'll say don't run this test and then, well, don't run this test, they end up with a lot of tests that aren't running, and eventually they declare bankruptcy and they delete all the tests and start over. I know of a project that has done that twice, they didn't learn the first time. Now they've got too many people working on the project, they've got something like 12 programming teams, plus a couple of maintenance teams, it's a messy situation, so the reason that I came up with this topic in the first place, Lisa Crispin asked me to speak at the Agile Alliance Technical Conference last year, and so I said sure, and then she called me back a week later and says okay, what's the title of the talk, and so I thought well this is an important topic, now when I thought that, I didn't think about how much work it was going to be to prepare this talk, but I had to develop a code base to support these concepts, and one of the problems is that when you look at the tutorials online, you have tiny little problems, and so they don't show the sorts of things that get in your way when you work on the same code base and continue to write automated tests over a long period of time. And some of them get people into very bad habits, so that's where this talk came from. Now the first time when I did it at the Agile Alliance Technical Conference, I thought oh it's a technical conference, there will be enough laptops that we can pair and we'll have a laptop for every two people. No, we didn't. There were several laptops in the room, but then I don't think there would have been enough anyway, but Michael Feathers got sick that morning, so everybody that was going to go to his talk came into mine, and so there were a whole lot of people that weren't prepared at all. So I gave up on that idea, so we should try something a little different. My laptop is down here, and what I want to do is take a mob programming approach, and so I need a driver who will run that laptop, and we can switch around, we don't have to have the same driver the whole time, so if you get tired of doing it, you can say so. Who's willing to do that? Okay, so if you need any help, I can come down and help out too. So I've got both PowerPoint and Eclipse running already, so we can alt-tab between them when we need to. Here it is. So where does your project start? It starts with an idea, and so when I decided to do this, I needed an idea for a project to be able to illustrate these things. Well, I didn't have any burning need for a project, and I didn't want to do anything too complicated because I didn't even have anybody to pair with on writing this code, and the code shows that. You'll see. So I decided, you know, well, let's have a little fun with this. So this project is about generating horoscopes for race horses. So how many of you have ever gone to the horse track and bet on the horse races? Anybody? Yeah, I've got about the $2 ticket, and I broke even once. So, you bet on a horse to win place or show, and then depending on how they come in, then you may get some money back based on how much you bet. Well, you can go to the race track and there's, as you're walking in, there are people selling you tip sheets that have all sorts of numbers about the horses previous races and who they raced against and what the weather conditions were and how well they did and all this sort of thing. So that's one way of trying to figure out what horse is going to win. Well, last time I went to the race track was many, many years ago because I was not very good at this, but my girlfriend at the time, so we took turns picking the horse, bought a $2 ticket for each race and actually she's the one that made us break even, but I would look at all this information that I didn't know what it meant and pick a horse and she'd go down and look at the horses and say, looks like that one wants to run. So she did better than I did, but I was thinking about this, there's a whole lot of luck in this. Any given day a horse could be having a good day or a bad day, there's a whole lot of luck in this. So it seems like a horoscope, connecting it to what the stars foretell and stuff is an ideal way of picking a lucky horse. Is it this horse's lucky day? So that's where I came up with this idea. And I actually, I think I got this idea out of a book years ago, but I haven't been able to find it. But this has been in the back of my mind, so I started writing this. Then I Googled it and there are several companies in this business, it turns out, of providing horoscopes for race horses. But it seemed like a good idea. So how many of you are familiar with behavior-driven development? Okay? So this is an exercise that Matt went and developed called example mapping. So you get the three amigos together and it's sort of a way of structuring the conversation. So the yellow cards up top, the customer can get a horoscope. That's a user story. That's a pretty basic user story. We're starting from zero here. And then the blue cards, horoscopes are for a given horse, effective a given day. That's a business rule. So that's a business, you might have multiple business rules for a given story. And then the green cards are the examples, the scenarios that you think are important. And the red cards are questions that you come up with. So these are things that ultimately need to be answered. But none of these needed to be answered for these particular stories. So this is what I started with. I drew up these cards and started to implement. And you can take those scenarios and you can turn them directly into tests using cucumber, using gherkin. If you're using some other tools, then you have to translate them into code. They're no longer readable by the business people. And that's why I like using cucumber. But these concepts still remain the same. So you see that I've got three scenarios at the bottom all marked ignore. Because I was doing this, then I realized, oh, getting two horoscopes is a big step from nothing when we haven't even gotten one yet. So I created a new scenario that really wasn't part of the acceptance criteria of the story, but it's just a step towards it, of getting one horoscope and just making sure that it's not empty. It seemed like a reasonable first step to me. What do you think? Does anybody have any ideas about a different place to start a project like this when you have no code written whatsoever? How many of you have worked on a project when you personally started on it that was zero code? That probably happens less often than we think it does, doesn't it? Coming into a project is already a bunch of stuff. It may have been running for years. But I wanted to start from nothing here. So we've got this. This is a feature file. Are you familiar with cucumber and feature files? Anybody not? Feel free to ask questions anywhere along the way. I don't want to leave anybody behind. And I'm sure we've got a whole wide mix of experience in here. And I may skim over some stuff thinking people know it. And somebody doesn't know it. The opposite is I may be going too deeply into something that everybody knows and tell me that too. Well, it's really very simple. This file, this is a test file right here that you see. So it starts with the word feature. That's a key word. And then there's the name of the feature. And this is a simple one. You can put a description in there. I don't have one here. And then the scenario. So these are the different scenarios. So there's a scenario and then the name of the scenario. And you can see I've got a description of this scenario. And then there are three basic key words within a scenario. This one's only got two of them. So there's given, which is the three conditions. There's when, which is the action that we're testing. And then there's the then, which are the post conditions we want to verify. So what we're doing here, we've really got no setup. Just we say, when we request an arbitrary horoscope, so for any horse in any day, don't care what that is, then the horoscope should not be empty. So that's not asking a whole lot of our code. But it's a good first step. So that when and then statement there end up calling these methods. So you see we've got at when we request an arbitrary horoscope, then there's a Java method underneath of it. And so when Cucumber is executing the scenarios, it will call this Java method. That's how it matches, you know, gets down to the code and exercises the system itself. And then we've got a then method also that it should not be empty. And we've got a before here that runs before each scenario where we're just setting things up. So we need, horoscopes are supposed to be based on the stars, but for some reason I ended up implementing it with a crystal ball. But, you know, I'm not, that's the way it happened. I didn't have anybody pairing with me on this. So I didn't, you know, sometimes I ran with ideas that I hadn't thought through fully. But you're, you know, feel free to notice, you know, point out things that don't quite make sense in here. Because I think we can learn from those things. There's a lot of different languages that support it. Yeah. Cucumber actually originated in part of RSpec. And then it became its own thing. I'm not going to do that. I used to test Java code using Cucumber running in JRuby so that I could call Java code directly. There was a, the first version of Cucumber that supported Java natively was kind of clunky. They threw it away and they started over. SpecFlow is actually part of the Cucumber family now. It had separate beginnings. There's a Cucumber JavaScript. There is, for PHP, there's Behat. You know, I don't know where that name comes from, but I don't know. I don't know if Cucumber's got that. I've certainly, I've supported calling services on a mainframe with Cucumber. You know, having Cucumber running on a different machine and just doing, you know, at the service API level. I did work with a company one time and they had sort of a homegrown test framework where they were encoding what had to be done in a big X of L statement and sending it to a, on a soap call and asynchronously getting the results back later. And we managed to drive that with Cucumber. You know, where the Cucumber was setting values in that XML and then shipping it across. And we had to cheat a little, we had to put one more statement that, at the end, that would wait for the this call to return, you know, get the return message and then look through it and, you know, check that the, you know, what the results were. So it was, it was not quite clean, Gherkin, but very close. So all this is doing, you know, it's setting up the parameters that we're going to call and then we're asserting that it's not an empty or an all string on the result we get. Pretty simple stuff. Yeah, so, so Cucumber has the ability to generate your step, this is called step definition. And Cucumber can generate those for you. Now, what's in the string is a regular expression. So this regular expression is very simple. It just says it's got this string and it starts at the beginning and it goes to the end of the line. The, the carrot is the, it means beginning of line and the dollar sign means end of line. So regular expressions, you can actually use just plain strings in Cucumber. But regular expressions lets you do a little bit of stuff and you don't need much regular expressions for Cucumber. But this is what matches it. The name of the method is completely arbitrary. Cucumber doesn't care about that. But when it generates one, it generates one based on the step just so that the people can say, oh, yeah, that, that makes sense. You know, if, if you're tracing through the code or something, you say, oh, we're in this method, that's the step definition for this step over here in, in the scenario. So here's our implementation, our first crystal ball. Impressive, huh? So are we ready to ship this to customers? Will they be happy with this? So, so when I was a kid, then we had these toys called a magic eight ball, which they still make, but they're made much cheaper now and they don't work as well. But they, they were, they were a black ball that had a flat spot on it with a little window. And you turn that over and there was a thing that would float up and it had, I guess it had eight sides or something. And so it would float up and show up in that window. And one of them was situation cloudy, try again later. So the ones they make these days, a lot of times it doesn't land quite right. You can't read it very well and stuff. They work perfectly in the early ones, but they were expensive to make. So they cheated it. So I guess use that for a string. This is a, you know, as a placeholder for, well, we really don't know anything. If we get that as our horoscope, every time we call a horoscope, then the customers are not really going to be very happy. But what have we gained with this? With this beginning? Right. So we've got things hooked up. We've got a class we can instantiate. We've got, we know the tests are hooked up. They're running. So, and we've, we've started to create an API for our system. We know the, how we want to call this based on the business needs. So, so why don't we, why don't we switch over, do a, switch over to eclipse, control tab. Now, what I've got right here is what's, this is, this is the complete project as it stands right now. So click on this package explorer tab. Okay. And then right click on that and go down to team and switch to. And we want first scenario. So what I did was, this is, the code is all in Git. It's up on GitHub. The code is in Git. And so I create, created branches for the different points. Now one of the things that turns out is really hard for doing this code base for this session. Normally, when you're doing a project, if you're, if you're going along and all of a sudden say a tool changes version and you've got to make changes to accommodate that. Well, you just make it where you are in the development and move forward. I have to go back and do it at the very first stage and then ripple those changes through all the other branches. It turns out to be a huge amount of work. I don't recommend doing that for your general development. You know, live in the present. But I have, through this we need the entire history so that we can go back and visit it. So we can go to this little run thing. There's a drop down there. And do, do run not ignored. And this runs our test. We can expand this. And you can see we've got, we've got a feature with a scenario in it and two steps. And everything's passing. This is the console output of Cucumber down here. And so it's running. So what would we do next? And you can, you can look at the code if you want. Does anybody want to take a look at the code in the IDE? So you can see that we've got, this is set up so it can be run under Maven too. I personally hate Maven, but it does run under Maven. It seems to be the way of the Java world now. So you can see that, you know, there's a source main and a source test because that's the way Maven expects things to be. And then there's Java code and there's resources. So that's, that's sort of the basic way of setting things up. There's a lib directory that has all the jars in it. And Maven's not happy with me because it likes to be able to pull them in from the Internet. And there's some jars that don't live on the Internet that are in there. And so it complains about that if you run the Maven. But I didn't want to, this isn't about, you know, this session's not about Maven so I didn't worry about that. So if we look at this, we can see under source test Java, we've got run kukes test.java. And what that is, is it's a, it's a connector between JUnit and cucumber. So that when you run JUnit, it can also run your cucumber test. It's a, it's a, it's a pretty simple thing. And then we've got a step definitions file that contains the step definitions. Up under resources, we've got the feature file itself under source test resources. Yeah, that's, it clips, it makes that hard to do. Let me think I may have done the wrong thing. This is one of the words of eclipse that they've never done anything about. You should be able to just increase the font size, but no. No. And I had set it up with a larger font size, but that piece too is all the way. Okay. I don't know how to do it over here on the left side. That's, that has escaped me. It's, you know, it's, it's what it is. That's a little more readable. So where were we? So we were thinking about what would we do next? Now that we've got this new basic structure, not really doing anything, but we've got stuff wired up. Now we want to make it actually start, start doing something, right? So how would we go about getting to that next step? So if we look at the code, if we, if we jump back over to crystal ball.java, then you can see we're taking a horse name and a date. Now they're just strings. That's not a great idea, but that's what they are right now. And, and it's returning a horoscope. Now that horoscope is the same all the time. And we want to make it so it returns a different horoscope to a different cause. Well, as a matter of fact, yes. Because, you know, there's luck in pseudo random number generators, right? So, yes, that's how it's actually implemented. But we need to, a way of driving our development forward. So, so, why don't you command tab back over to the presentation. So these are embryos starting with, you know, the first time it, it, it cell divides and it's got two cells. And then it gets more and more complex. So we're trying to take these little tiny steps to grow our application and grow our tests so that we're moving forward, pushing our application along with the test. So, I came up with several options that we could possibly do. We can triangulate using scenarios. We can use the other scenarios we've got. And that will force us, when we get to a different horse, same day, it's going to say, oh, it should give us a different horoscope. Well, it's not going to give us that right now. Or we can switch to unit tests. And then there are sort of two styles of unit tests. There's interaction based tests and there's state based tests. Interaction based tests are saying, oh, you know, this, this should call this, you know, basically obey this protocol. And we're calling, you know, some supporting object. And state based tests says, okay, after we've done this, and we expect this to be true, and examine state in an object. So these are sort of two different styles of doing this. Martin Fowler has a great article on his site about these two different styles. So option one here, triangulating using scenarios. I see people go down that path all the time. You know, we're already writing cucumber. So it seems natural. Well, we'll just write another scenario. But what I find out is that that's a big step. And so then they say, oh, well, we'll create a lot of other scenarios to test all these little things. So it ends up there trying to, to test the insides of their, of their domain model all through the API. And that gets really clumsy very quickly and hard to maintain. And, you know, it kind of restricts things. So I tend to switch early to unit tests. Now, when I first started doing test driven development then, then, well, there were no mock optics then. They hadn't been invented. Now, I came from a hardware development background. And so having stubs and fakes and stuff or things I was used to in hardware development, you know, if you're doing a transmitter and you need to be able to run it without an antenna, you need a dummy load or it doesn't work right. It can blow it up. So there are those sorts of things, you know, that to be able to, to take part of your new system and run it without all the real stuff around it. Now, true mocks also verify the behavior. And when you're using the Detroit style, then the verification is, is built into your test after the fact rather than setting it up before the fact in the mock. So those are, that's the basics between the two styles. So, well, let's, let's go back to Eclipse and go to team and switch to, and rudimentary first unit test. Right, right click on the project, yeah. And then team. Yeah, this is a little clumsy. Switch to rudimentary first unit test. And so that changes the underlying code. Now, if we look at source test Java, we see we've got a new package now. And inside there is a, is a J unit test. So you can open that up. So this is a J unit test. And if you look at it, it's basically doing exactly the same thing as our first scenario. Does that make sense? Is that really a step forward? I mean, some people will argue against that. To me, it says, okay, I've, you know, I've got J unit set up right. And I've got a starting point for being able to do other things. And I can take smaller steps in J unit than I can in Cucumber. Or more easily anyway. And nice thing about doing it in J unit is that if I refactor stuff through Eclipse, it's refactoring the test code at the same time. So here I've got, we've got the same thing. We get a crystal ball. We, we fetch a horoscope. Here we're just sticking strings in because we don't care what they are. And we're making sure it's not, not empty. So, but, well you mentioned we want to be able to get random horoscopes. So we want to use a random number generator. So I happen to have a library here that takes a, it takes a, a language file, basically the same kind of file you would use to parse a language, except it uses it to generate text instead of to parse text. And so we, we create that and, and it's, it's random in there. You know, it, it, it's got place holders so you can generate complex sentences and stuff. So very parts of the sentence all based on the random number generator. Well, how many of you ever tested anything that's run with a random number generator? Yeah, it's hard to do, right? How do you know what, what's the right answer? Yeah, so you just say, oh, does this seem right, you know? But it's hard to do. There's, there's some strategies for doing it. If you can control the random number generator and give it the same seed, then you can make sure it does the same thing. But that's kind of fragile. Unrelated changes can all of a sudden mess that up. You know, some other piece of code makes a call to the random number generator that didn't happen before and it takes one of your random numbers so you're no longer getting the same sequence. And so it breaks it. So I don't like that. So, so I would rather have tests that, that are deterministic rather than based on, on random stuff. So I know that I'm going to be having to delegate to this library to actually generate my, generate my horoscopes at random. So command tab back over to the, so I know I'm going to be calling this external component. This is a problem that you, you know, comes up in a lot of, a lot of times. So this is sort of a general thing. This external component is really hard to test. You know, we may not control it. I've got to hear it's just a jar file. I didn't write it. A friend wrote it. We were, we were playing around with this idea some years back and we never took it through to completion. But he spent one night, he just said, oh, let me see if I can do this. And he says, oh, I've done this part of it. And so I've got the jar file here. But, you know, it calls the random number generator inside that. And I don't have any control over it. So that's really hard to test well. But, so the domain logic is in crystal ball. There's not much here yet. And the horoscope provider, you know, is our external component. So I use the adapter pattern. This is from the Gang of Four Patterns book, where we separate two components with an adapter that adapts from the needs of our domain logic to the API of the external component. And this is, you know, this is a bread and butter design pattern. And I use it all the time when I'm depending on something that's not under my control. Because it isolates me from, you know, whatever may happen in that component. So then we can test the domain logic with a fake adapter that we control from the test. And that way we get our, that adapter API to be exactly what we need for the needs of the domain logic. Now, that means we still have the adapter calling the external component. We need to test that, too. We don't have to, we don't have to do the same level of testing there. If we expect that that external component really works, we don't have to test all the edge cases and stuff. That's somebody else's problem. They should have done that as they were building it. But we want to test our use of it, our expectations of it. Like, we want to be able to say if we call for a horoscope that it's not empty, not blank. That's basically what our expectation is here. Now, this pattern has saved my butt many times. I was working on a project once and I was writing the order entry part. The team I was on, we were writing the order entry part. And then another team was writing the part that actually implemented, you know, provision services that people bought. So this was all automated. So we took an order in, we see what services they needed, and we had to call the services component and it would go out and set that up for them. Well, we got a, you know, a new jar from them one day. And all of a sudden, all my tests broke. A lot of things the tests broke. And so I went over to the tech lead of that group and I said, on this new jar you gave us this morning, you know, what changed? He says, oh, we just added this new little bit over here. And I said, I've got these tests calling it and they used to work and they're not working anymore. What am I doing wrong in my tests? And so I showed him the tests that were testing my adapter and showed him that adapter and, you know, these tests aren't working anymore. And he looked at it and he says, oh, we must have broken it. And he hadn't tested that. So, you know, this whole thing from putting your new jar in and going and talking to him and resolving this, maybe it was a half hour. If I didn't have those tests, I might have spent three days trying to figure out what was wrong with my code and before I discovered that it wasn't my code. So, breaking things up between the stuff that it's part of this domain that you're working on and stuff that it's using is hugely valuable. And you can do this, I mean, you don't need cucumber and Java and JUnit and stuff to use this sort of pattern. You can do this with anything. So, here we create a fake horoscope provider. This is our fake adapter. And all it does is return a string. It's hardwired to one string. So, we're kind of just pushing things down. Have you ever watched anybody do pottery on a wheel or done it yourself? So, it clay is really elastic and you start out with this lump of clay and it's spinning and you push on it and it changes the shape, you know, and ultimately you end up with a beautiful vase. And working test driven is sort of like that. We make the test push on the code until it gets into the shape that we really want. So, we do it a little bit at a time. This fake horoscope provider, we've pushed, in our original crystal ball, we were just hardwired to generate a string. Now we've put it externalized that into this horoscope provider and we're making sure that our crystal ball actually goes out and gets it from that horoscope provider. So, then we can plug in a different horoscope provider and we're not worried about that. We know that it's actually calling that. So, here's what we end up with. Our test is providing the horoscope provider, the fake one, and then it's calling the crystal ball. The crystal ball is calling that fake horoscope provider and so our test is in control of all this stuff and we can do other tests that do it different things. So, this works, you know, and the test is in charge of its own thing. It controls the data that it depends on and that's hugely valuable when you're working on stuff. So, then we've got the adapter, this third party library that generates the horoscopes is called Mumbler because it's not really making sense, it's just mumbling. So, we create an adapter that we'll call it and assert that it's not an empty string. That's all we really care about in this point. That looks like. We're testing this other adapter and testing this third party library that we can't control. So, but we can still make assertions about basic characteristics that we expect, in this case, that it's not an empty string. And you notice that Mumbler's API is called load rules and generate. That doesn't make a lot of sense from the point of view of our horoscope program. That makes sense from the implementation of that library. Our adapter changes, you know, the API to the horoscope for. Because we don't care about the underlying implementation in that library, we want it to be easy to call from our domain and make sense in our domain. And so that's what the adapter pattern is for, is to adapt from what we want to what we've got. So, there's several patterns we've talked about here. These patterns are generally useful. So, I call this protect the border. So, on the outside of what we're working, isolate the domain model from the dependencies of other things that we have to depend on. So, the adapter pattern is the most common. There's another pattern called the mediator pattern, which is bidirectional. And, you know, there's less call for that. So, and then test the domain model using a test double adapter mediator and test the integration aspect of the real adapter with the external dependency. So, I mean, this is a pretty simple example of these patterns. But, you know, I've used these in all sorts of situations and I find them generally useful as we start depending on other components. Okay, the sort of jumping to a different part here. Do you want to take a look at the code in more detail before we go to the next topic here? We can, if you want to switch to Eclipse and switch to refactoring first scenario. In fact, why don't we run that? Why don't you run the, run the, the not ignored tests up in that this bottom here? Yeah, number one, run not ignored. So, you can see the tests that are running. And you can click on, you can't click on the feature because that doesn't work in Eclipse, but you can click on the J unit test and it'll take you right to that code. So, we've seen, seen a lot of this already. So, go to this one, a sure generator has variety. In the dependency, if something changes, then I'm only checking the things that, that I care about in that dependency and making sure it, you know, it follows. If something breaks my assumptions about it, I want to know that. If the other things change that don't break my assumptions, that's fine. This test, you could say it's a funny test. I'm calling it five times and making sure I get at least two different horoscopes. I ended up writing, that's not in the slides, but I ended up writing that because I had to generate the rules file that it's following. And that rules file started out as something very simple that only generated one horoscope. So, we're skipping a little bit here because otherwise it's like watching paint dry. So, let's, let's go back to the presentation. So, look at another aspect here about the layers of your tests. Because this is another really, this is one of the things that as you get more and more complexity, then you can run into problem. So, we've got a feature file, which is our Gherkin. It calls, when Cucumber runs it, it matches it up with step definitions, which then tests the system under tests. And so, the feature file describes the business intent. The step definitions contain the incidental details, you know, about how it's implemented. And then the system under test is what we're checking, crystal ball.java. So, right now we've got, get a horoscope.feature and step definitions.java. It's called step definitions.java just because there's only one set of step definitions right now. One thing I've seen is that as people add more steps and more step definitions, they just throw them all into the same one file that they had. And it becomes very hard to tell what's going on because you've got all sorts of unrelated things in that file. Okay, right. So, I work on two levels. Yeah, it's kind of interleaved. And all this, and I don't write code in the system until I have a test that demands it to be there. It's pushing on the system and saying the system should be in this shape. The system should do this now. Now, I've got ideas about what I'm going for. But I don't actually implement it until the test requires it. And that's how I end up with not only extremely high code coverage in my tests, but the concepts that I care about, which is what really matters. You know, it's easy to write tests that cover lots of lines of code, but don't do any testing. I've even looked at tests and I scratch my head and then I commented out the line that called the system under test and the test goes past. It was only checking the setup data. And they didn't notice it because it was too complicated. And they didn't notice that they weren't actually testing anything. So, you know, that's why I drive things so much from the test because it protects me from making that kind of mistake. Not everybody does that as much, but I think you'll find that the patterns I've learned from do it working that way are still valuable. So, then I go to a four-layer system. So, ultimately, you get the step definitions need to... They start having some commonality where they need some of the same behavior in more than one step definition. Well, you don't want to just duplicate that on all the different step definitions. So, you want to extract that code. And people typically call this a test helper. So, in your step definition, it just becomes one or two lines that cost the helper code. And it contains the incidental details. And we're starting to run a little shorter time on this, so I'm going to go through this a little more quickly. We won't explore all the code. So, we've got a retail API. So, when we're actually accepting credit cards, then we've got this that goes through a retail API adapter to the system under test. There's another one that's a retail web adapter. So, I've got two versions of that adapter to the system, one that calls at the API level and one that calls through the web interface using Selenium. Both, through the API is much quicker, of course, you know, and less error prone. But we can also test through the API. Why don't we... Why don't you go back to your clips and let's take a quick look at that. Let's go to full transaction. Look at the retail step definitions, which is down here. And so, it's getting a retail API. We're using a little lightweight dependency injection framework to be able to poke that in which API we're using. So, we use a different one when I'm running suite of tests through the web interface than when I'm running it through the API. And then we can look at the Equine Horoscope retail web adapter. And so, this is creating a web driver and then calling, finding things on the page and calling that. So, ultimately, let's switch back to the presentation. So, we started out with very simple thing and then it grows. And we end up with three different feature files. One getting a horoscope, one paying with a credit card, and then one that kind of combines both of those aspects to be able to actually buy a horoscope. So, we've got three different feature files. We've got three different sets of step definitions. One that's about the crystal ball. One that's about, you know, the retail aspects. And one that's about just the credit card aspects. You know, we have to go out to a merchant bank and say, you know, is this a valid credit card? Can we charge to this? We've got then some code that's shared between these different step definitions. Ended up with a class called horoscope collector so that we can see what we get. And we've got another one that contains the credit cards that we use for testing so that we don't have to duplicate these things. Now, none of these things are called helper anymore. They now have names that reflect what they're actually doing. So, the scenarios are divided into cohesive features or subfeatures. It's not a one-to-one between a feature and a scenario, a feature and a step definition. Because here again, we want to think about what you're doing. We delegate code that is useful in our tests. So we keep, we maintain our test code with the same rigor that we would maintain our production code. Because we've got to live with this for as long as we've got to live with the production code. Sharing data definitions. And here, then we can plug in different files for different testing contexts. Testing through the API or through the web. So now I've got a four layers of tests here plus the system under test. So what I've done is split the incidental details part into that that's related to the testing. And that horoscope collector is only related to the test facts. It's just a way to collect the horoscopes I get back and then be able to examine them and see what I got and in what order. Then we have an adapter that contains incidental details related to the system under test. So I've split these two concerns out to help me keep things straight. Because otherwise, these test classes get bigger and bigger and bigger and it gets harder and harder to read and tell what's going on. So we have to maintain the concepts that are in our mind in the code and in the organization of the code. So here we've got for our system adapter, we've gone to a page object pattern. So finding stuff on the page. Well, that's related to the system under test. And if that page changes, we want to be able to quickly go and find that and none of the rest of our test code here. So anybody know what this is? Yes, the agile test pyramid. How many people use that in their work? You ever look at that? Yeah. So this is something that you know, people talk about, you know, oh, this is, it's the ideal distribution of your tests. And I don't know if it's ideal in all cases. What I found, though, is that it's really hard to drive your development from this idea. You know, people, you look at this and say, oh, well, we need, we need more of this kind of test. Well, that's not the point. The point is to think about what you're testing. Testings as much as possible, at the lowest level possible. And at the lowest level that makes sense for what you're testing. Sometimes you repeat some of the same functionality through a larger part of the system at a higher level test. And so when I developed this code, then I went back and I just counted the test with in what goes where, and it came up to an approximation of the pyramid. Now, one other thing that here, you notice there are four tests that test the test code itself. So we talked about that that horoscope collector. It needs, you know, it needs to work. There's, this is a shortened version of the talk to fit the time slot. And so we left out all the stuff about the database access. So there's stuff there that needs to test code that needs to be tested. And that's done with an adapter pattern also. So that I can test without the database and then, you know, and also run it with the database. So I see the test pyramid as a way of sort of checking, well, what does this look like? Not as what drives what test I need next. The bottom line is we want to grow air systems. We don't want to try, you know, make it all flash into being at once. You know, this is not magic. If we grow with a little piece at a time, then this is not a new idea. This, you know, this goes way back to incrementally grow our systems. And here we want to grow the test with them. If we're driving it with tests, we grow the test with them, keeps them both in sync. It keeps keeps everything making sense and think about it as we go along. Now there are places where this code probably makes perfectly good sense to me, but it may not be clear to somebody else. And that's because nobody else was looking at it when I was developing it. It's hugely valuable to have other people looking at it, have some diversity in the group working on it. But you want to always keep both of them working and working together and keep them organized in ways that make sense to everybody involved. And you may end up spending an entire afternoon trying to decide, oh, this part seems, somebody says this part seems to be getting messy. What are we going to do about that? And you may have to do some thinking about it and say, well, let's try to, you know, this, this is all we need right now to sort things out. Or now. But as things get more complex, you're willing to go back and revisit that question and then maybe do some more. And if you go through this code in more detail, you'll see that that has happened as it evolves. Now, I wrote a book on this when I was developing the talk. And so as a participant here, this coupon code will give you a free copy of the book, which has, has stuff that's been left out of, of this presentation, the time constraints. So you can download an ebook, you can download it for free. This code's good for about a week. But feel free to download it and, and it can remind you of the things we've talked about. And it can go, it goes into some other patterns too that we didn't talk about that I had to take out of this presentation for today, it's just a 90 minute slot. So is this, do these patterns seem helpful to you? Did you get something that you can take back and use at work? Is anybody going to do something different next week than they thought they were going to based on this? It's kind of hard, you know, when you've got a big project already going to make sudden changes. But you may want to, as you work on it, you may want to think about some of these patterns help you make the test code clearer. Make it easier to find things. Make it easier to understand. You know, one thing I think about is what happens when a new team member comes on board? How long does it take them to be able to understand what we've got? And that's significant. How many people have worked on on a project and then gone off and done something else and come back to that project, you know, much later? You done that? It teaches you a lot of stuff, doesn't it? I mean, one time, you know, six months had gone by and I'm working on a project and I see something that goes, why did I do that? I said, this is so complex. I can simplify this. And so I simplified this before I was doing test driven development. I simplified it and it was much simpler. And then, you know, then I tested it after, well, but there's one case that doesn't handle. And so I start backing out those changes. This seems awfully familiar. And I've realized that I had gone through this six months before and made this same simplification that didn't work. So then I put a big note in the code. Today I would write tests that show that and I would see this much sooner. I would let the test catch me. I've gone back to a program. I had a company I'd worked for had gone out of business and a guy who was both a customer and a sales rep called me up, looked me up and said, hey, I've got the state of Florida and I want some changes made to this. The algorithm in this code. Can you do it? I said, well, if you can buy the rights to the code, sure, I can do it. And this was embedded assembly language. I hadn't seen this code in over 10 years. But, you know, attention to code hygiene and making it easy to understand, you know, when you work, I'd done that code solo. And I also, it was a small company, I had to do customer service, you know, I had to help the technicians, repair technicians when they had a problem. So it teaches you a lot of stuff about the problems that come up. And you have to deal with your own, with the pain yourself and it makes you fix the problems. And it really was a great education. So you can use this, be sensitive to the things that make, make the job just a little harder and try to clean those up as you go along. Now, having a team of people working together can help you notice these things more easily. If you've got good communication in the team. Because you've got different opinions and you say, oh, well, this is perfectly clear. And somebody else says, well, what does it mean then? So having diversity really helps. Any questions? Yes. So that's a hard situation. The first thing that I would say is don't expect to ever catch up. Don't expect to solve the problem completely. It just, you know, it's hard to, it's hard to keep up when you start from scratch. You know, and always have good tests and always test everything that you need to test. It's really tough. It's a lot of work. And when you've got code that doesn't have automated tests and you're coming long later, then it's a pipe dream to think you'll catch up. So, and that kind of frees you from having to be complete about stuff. Instead, focus on the things that you're working on. The things that need to change. Or the things that you worry about. The only time that I think a record playback test is worth anything is for legacy code. Just have it go, you know, record somebody using it and then you can tell if anything changes in that. They're lousy tests. They're not maintainable tests. You know, if they break you don't know what broke. But they can alert you to changes so that you don't have to do better testing on that right now. And that can, you know, if something breaks accidentally, oh, I'm alerted to it. But as you're changing code or testing new thing, that testing new functionality, then do the best job you can on what you've got. Try to isolate that area from other parts. And so the adapter pattern that we use to isolate from third party dependencies, you might find that you want to use that within the code to isolate this area that's that you're making clearer from sort of this soup of legacy code that's around it and treat that like it's a third party component. You know, now the process of cleaning up legacy code is its own challenge. And there's two good books on that. One is Michael Feather's book, Working Effectively with Legacy Code, which will give you lots of techniques for dealing with that. And the other is called the Mikado method, which is a way of approaching it so that you can relatively safely make changes in legacy code and not get lost. And basically it creates a directed acyclic graph. And so you try to make a change, oh, well that won't work because of some other thing that needs to be changed first. You undo that, you go make that change. Eventually you get down to something that you can change and then you work your way back up the graph to the things that that enables. It's a great book. Those two, when they came up with this book I thought, oh, that's what I've been doing in my head when it works. But they did it, you know, by drawing it out and made it a much simpler process. But yeah, it's hard. And the same thing down at the lower level, same same sort of process. When you go in to, you know, tweak something at a lower level, that's also a time you can look at, oh, well what else needs to be tested in this small piece of code in the unit tests, you know, because it's got behavior that's not tested. And doing that down at the low level, then you make that component more robust so you have less worries on the larger levels. And, you know, you can easily test the edge conditions of a component. Yeah, okay. So here's my contact information also. So yeah, any questions that come up, let me know. Do, I encourage you to get the book, you know, with the free code I don't make any money off that. But, you know, I wrote it for your benefit. So, thank you. Thank you for your participation. And thank you for driving.