 Hi, welcome to Visual Studio Toolbox. I'm your host, Robert Green, and joining me today is Phil Jepixi. Hey, Phil. Hey, Robert. How are you? Thanks for coming back. Hey. Thanks for having me. Phil was on the show a few months ago. I don't remember exactly when. We did an episode on design patterns. We did? It wound up very, very popular. We got a lot of feedback that we should do more of these. Yeah. That talk was really a whirlwind through just a few design patterns. We spent a lot of time talking about solid and why design patterns matter. So what we've done is come up with a whole bunch of episodes where we do a deep dive into individual design patterns. Yeah. I said, yeah, that's great idea. We should have Phil back. We'll do a bunch of them. Yeah. And here you are. We're going to do a bunch of them. Here I am. So this is the first of a series of episodes. We think it's about 10. It may wind up being a little bit more by the time we're done. We're taking a couple days here to shoot all of these back to back to back, which is why we're sitting, by the way. Right. The astute among you will notice that we're that I'm actually sitting, which I never do. But if we're going to be here all day talking for a couple of days. And I'm old, so I need to be able to. And I'm older. So we have a number of design patterns, but we're going to start with an episode on test driven development and behavior driven development. Yeah, or test driven design. Okay. It depends on how you. TDD or BDD or BDD. And, you know, it really doesn't fall into the realm of design patterns at all. But a lot of the design pattern shows are going to be using unit testing to show how the patterns work. But also I think it's an extremely important thing to talk about is testing our code. Code quality matters. Right. And it does fit into the whole design of this series, which is ways to become a better developer. Right. Absolutely. And, you know, I always start my TDD or BDD talks with this quote from Michael Feathers. And I think it's brilliant. The main thing that distinguishes legacy code from non-legacy code is tests, or rather a lack of tests. And it really lends itself to how we want to do modern software. Back in the day, and some of you probably are unfortunately still in this world where you write a whole bunch of code. You throw it over the wall to QA. And then QA does its thing. And then they throw bugs back over the wall. And it's really a ping-pong game. Now, let me stop you there for a second. As you said, modern development. So, but we're not talking about techniques you would use building.net core apps only, right? Correct. So sometimes when we say modern, we mean, you know, this cloud-first, mobile-first world of containers and microservices and serverless functions and .net core. And if I'm watching these and I say, well, I don't do any of that. So is this for me? The answer is yes. It's modern practices, modern development techniques. Right, so outside of the world of Microsoft, modern development has a different meaning. And what I really mean is what we should be doing today. Right. Right, in this day and age. Okay. We've got the tools. We've got the techniques. We just have to utilize them. So if you're doing vb.net windforms or you're doing vz-sharp microservices and everything in between, everything we're going to talk about in the next 10 episodes applies to you. Right. Okay, so we're going to get that out of the way. Well, it applies to you if you care about the quality of your code. There you go. If you don't care about the quality of your code, these shows aren't for you. Or if you're not sure, if you care about the quality of your code, watch these and then make the choice. Right. Hopefully you'll choose that you do care. So somebody, I found this in a magazine, IEEE 2001. So the numbers are dated, but it shows the difference of how much it costs the company based on where you find the bug. So if you find the bug in requirements, let's just say that's 100 bucks. Easy math, right? If you find the bug in design, it's almost five times that. When you're coding, almost 10 times that. And you go all the way down to maintenance, right? It's significantly larger, right? And we know this, right? To fix something in production, you have to go through a release cycle. You could have been losing money because you were not charging customers, those types of things. But in reality, most developers don't care about these numbers. So I'm going to do the old triple X. You didn't get the right answer on Family Feud. And say, why do we really care about unit testing? And I'm going to say unit testing in general, as opposed to specifically testing first. Because there's also what we call test eventual development, where you write to code and then you write to test afterwards. But there's some problems with that, which we'll get into. The main reason that I really promote testing your code is for the benefit of the team. If you're writing something, and I'm depending on that code, and you've got tests around it, and I know that the code is working because your tests are passing, then I don't have to think about that. I can focus on what feature card I've pulled and the work that I have to get done. And it really builds up the team atmosphere. Because if I have to go around and say, Robert wrote this code again, and I have to check to see if it works, then I'm slowing down. It's causing friction. And you're creating discord among the team. Right. And also, if your code doesn't work, now you have to wonder how much of that is because of my code, how much is a bit just because of your code. Right. It's a reliability thing, and confidence. And confidence, right? So one of the things and one of the principles in XP is you have to have courage. XP. Extreme programming. Sorry. XP is one of the early ways of doing Agile or one of them. Not doing Agile because that's not the right word. XP is one of the original frameworks that help define the Agile manifesto and things like that. And he talked a lot about unit testing, but he also talked about confidence. If I spend my entire day afraid to make changes, then we're not going to advance the code. Right. You have to have the courage to fail, but unfortunately, without testing in place, failure means production problems. Right. Right. And I don't want to be that guy who broke production. But if there are tests in place, then I can say, hey, I think this is a better way of doing this. Let's try this. And I run a whole bunch of tests, and they all pass. Now I have the confidence I can make those changes. Right. And if the tests fail, I failed fast. I've got that rapid feedback loop going on. And I go, oh, I better not do that again. Right. We move on. Courage falls in the same thing as the confidence, but also cadence. And this one, I always have to spend a little more time talking about because one of the common friction points when we were talking about this last week when we were at a show together is management saying, well, I'm not paying you to write twice as much code. Right. How does a unit test affect my customers? Well, it speeds your development. And people struggle with that because they say, well, I have to write this unit test, and then I have to write the code, and that should slow things down. But what it really does is speed it up because development is not a typing skill. It's a thinking skill. And if I have the confidence that making this change isn't going to break things, and I have the courage to go ahead and make those changes, now I can just put my thoughts down into the computer and make those changes. Right. And I know whether they worked or didn't work right away. And then I can just revert if it didn't work or plow forward if it did work. So you really get a faster cadence. That being said, there is a learning curve. To really get into test-driven development or design or BDD, you really have to practice. How do you get to Carnegie Hall? You practice. Practice, practice, practice, right? So yeah, initially, it's going to slow you down. When we talk about test-driven development or test-driven design, we're really looking at the smallest unit of work that we can test, which in C-sharp is a function. You can't test part of a function. So going back to our solid talk that we did on the last show, single responsibility, you want to make your functions nice and small. You shouldn't be doing 12 things. You shouldn't be doing one thing. So we test that. And it's like a LEGO brick. So we test the LEGO bricks to make sure that they've all got eight bumps or four bumps that the bottom holes will fit another LEGO brick. And we have the confidence that that piece of code does what we expect it to do. Doesn't mean there won't be a bug, though, because people sometimes say, well, I have a test. There can't be a bug. There can be if you don't understand the requirement. So we still have to have that human factor of the conversation around the cards and the collaboration to make sure we're writing the right code. But at least with TDD or unit tests in place, we know that that piece of code does what we expect it to do. But there's a problem with just focusing on TDD. Now, I took this picture down in LEGO land, I think in Orlando. And I thought it was brilliant because it really emphasized how we can get tunnel vision. So if you look at this picture, what's wrong with it? Can you see? What's wrong with it? So we've got a submarine. We've got a captain looking through a periscope. Submarines have periscopes. And of course, you're going to open the hatch. Just look at the periscope, which means you better be above the water. Right, which defeats the purpose of having a periscope. Right? Unless you want people to know you're spying on them. Well, that's true. That's true. Maybe you just need that extra six feet of elevation to see above the waves. So I'm pretty sure that the unit test for the hatch passed, that the unit test for the periscope passed, and the unit test for the captain passed. But what didn't get factored in is the behavior. So a submarine should be able to raise its periscope and look above water while it's still submerged. This obviously does not fit that behavior. So something that came out a few years after test-driven development or design, something called behavior-driven development or behavior-driven design. And I guess we should back up and explain why I keep saying development and design. Development is obviously writing the code. Design is how am I going to make this API. And we're going to show that when we actually get into the code samples. But those are two different things, right? When you design an API, you're trying to think of the clients and how they're going to use it. When you're doing the development, you're trying to make that a working API so it does what you expect it to do. And you can get both of those with TDD. So what is called TDD and BDD from now on instead of repeating both. So with behavior-driven development, we start with the behavior. So I need a ship that can do the castle run in less than 12 parsecs, OK? Now I know the behavior. This is the big picture story that we're trying to provide for the customer. And even if we get that requirement, well, how do you eat an elephant? One bite at a time, right? So let's say that I know I need to build a Millennium Falcon. But as a developer, I can't just start coding Millennium Falcon class. I have to break it down into little parts. So in our head, what we will do is work our way all the way down to something small enough that we can program. And we'll program that LEGO block. And then we'll program the next LEGO block. And we'll keep going until we have all these LEGO blocks. But if you don't have that instruction manual and you're trying to build a 1,500-piece LEGO thing like the Millennium Falcon, and I've tried it because we lose all of our instructions, you're going to fail, right? You're not going to match that behavior that you need. So behavior-driven or BDD, you start at the outside and you start writing specifications. And there's several tools in a .NET world that do this. And I'm going to show M-spec. But you write all these specifications and you start at the outside, right? I need to do the kessel run in less than 12 parsecs. And I work my way all the way down to the secret cargo hold, to the guns, to the navigation system, the hyperdrive, all that type of stuff. When I'm at that lower level, I then start programming in a TDD manner to make all those LEGO blocks pass. But I have captured the context. So now I know how to put those LEGO blocks together. There's a couple advantages to this. First and foremost, I don't lose track of what I'm doing. Let's say I get hit by a bus. Now, I don't want to be hit by a bus, but typically in this field, being hit by the bus means that company across the street with a free lunch plan and better health insurance, right? So I leave. And Millennium Falcon's not done. And you come into backfill for me. And there was no overlap, so I can't sit you down and train you. You can actually look at all those contexts and all those specifications and say, I see what Phil was trying to do. And then you can pick up the ball and start running with it, right? When we had this discussion about, oh, sorry. The whole point of this is that for a fairly significant investment upfront, you wind up being able to spend less time developing overall because you are more likely to write the correct code upfront. Therefore, you will spend less time fixing your code because it doesn't do what you thought it would do. So yes, it's a lot harder up front. There's a lot to learn. You push the actual coding out. But in the end, you wind up building things faster because you're building things more accurately. That is correct. There was an interesting study done. And if I can find the link, we'll put it in the show notes. But it was actually done studying different teams at Microsoft. So we had the same technology base. We had the same corporate culture. And I looked at teams that were doing TDD upfront. Looked at teams that are doing TED or test eventual development. And looked at teams that had to. Test eventual development is you write the code and then you say, oh, crap, I need to do unit tests. And even though Visual Studio will create your unit test for you, so that's a little bit easier. You're still doing it after the fact. And then you're trying to make sure that the test covers the code and the code is correct. And one of the big problems with test eventual development is I have code. It seems to work. Why should I go back and now write unit tests? At least that's the thought. There are many reasons why we should, which we'll talk about shortly. But they did this study at Microsoft looking at these three different styles of development. And I don't quote statistics because 90% of them are made up anyway, but there was a slowdown in development for the teams that were doing test driven development. So they're testing upfront and they're doing BDD or TDD or whatever. But their releases were much quicker and the quality of code was significantly higher. Right. And so then you worked on the spectrum. So the people who were doing test eventual development were having a better time supporting the maintenance releases, but there were still a lot of bugs that were getting out. And then the typical QA, the QA ping pong game, I always joke, I said it's the largest unit testing framework in the world and that's called production, right? Well, Microsoft never... I don't always test my code, but when I do, I test it in production. Yeah. And Microsoft had a reputation for years, right? You would never pick up any Microsoft product prior to version three. Because that's, and this is the old Microsoft, the new Microsoft is very, very different. I'm very, very grateful for that. But you've got to know upfront what you're releasing into the quality. That being said, these are some of the common friction points and then you had some others that we brought up when we were talking last week at the show. First one, people say it's hard. And I always say that I work very hard upfront so I can be lazy down the road. It took me a while to really understand unit testing. And it's a pendulum, right? So I finally get unit testing and the pendulum swung the other way and now I'm testing everything. I'm testing properties in .NET, which I don't need to do. I have to assume that the framework works, you know, and it was slowing me down even more. So there's that sweet spot in the middle where you're testing the right stuff and that takes time to get there. But once I have it there, then I have all these benefits. One of the benefits I didn't mention is let's say that we've been working on a software project for six months and there's a thousand unit tests in place, right? Cause we're really being good about writing these unit tests. Business comes up and says, hey, we need to make a significant change. We're gonna rip out Oracle and replace it with SQL Server. And you go, okay. What's their next question they always ask? How long is it gonna take? And how do we figure that out? Well, you gotta start thinking about where's all the points in code that you touch the data and how much of that is generic versus specific to the database. So you gotta now slog through your code and make educated guesses and then flip a coin and flip another coin, take an average and come up with your best guess. At the end you got a dart board and you throw a dart at it and it hits something, right? And we jokingly have terms like wag and swag, right? Wag being a, I don't know if I can say it on the air, wild ass guess. Sure you can. Okay. Or a scientific wild ass guess, right? Cause we slog through the code. Let me propose a different way of doing that. We've been working for six months. We've got a thousand unit tests in place. Let's just rip out Oracle plug-in SQL server and run the unit tests. Now we're gonna have to write a little bit of code to make it work at all or even compile but let's say 500 of the unit tests fail. That's at least three months of work, right? Because it's half of where we have gotten so far. If 10 unit tests fail because we really paid attention to solid and we used an adapter or a facade which we'll talk about in a later episode, to front that database, then we could probably have it done in a week, right? So now we have numbers where we're measuring the impact and we can give realistic expectations to the business. Yep. The other one that's common is, you know, fill in the blank doesn't want me writing twice as much code. And yeah, you're writing more code. There's no ifs, ands, or buts about it but is that gonna help with the quality? And I think you're actually writing less code because you're writing code only once, right? As opposed to writing it five or six times until you get it right. I don't have time for it. That's a common complaint as well, right? We're under these incredible deadlines. We're assigned to five different projects. They all want something now. Yeah. I think that's gonna be the hardest part is if you're starting a green field project then you could say, oh yes, we're going to do this next project in this manner. But how often does that happen, right? You're probably in the middle of a project and now you'd say, oh, we should really switch over to this. Now you're not, are you writing code? What are you doing? Well, so you can switch in the middle, right? You can. Yeah. But it's kind of a combination of test eventual and test driven. Yeah. Do you want to talk about that now or do you want to get through this slide? Because we can talk about how you handle legacy code and jumping it in the middle. Let's do that at the end because we haven't talked about what it is yet. Okay. Or we haven't seen what it is yet. Here's one I love, right? My code doesn't have bugs. Of course it doesn't. Right? And I've heard that. I've had people say. Well, if you don't have tests, you can't discover bugs, right? That's true. That's true. So therefore, yeah. If nobody's testing it, I'm not reporting it. You can't have bugs. Prove it. This one drives me crazy. That's QA's job. Now, I'm old enough and I think you are too to remember when we got paid by line of code written and QA got paid by number of bugs found, which didn't really create a symbiotic relationship between the teams, right? It was really in us versus them and lots of gnashing of teeth and pitchforks and burning torches. It is the team's job to deliver quality code, right? I play goalkeeper three nights a week for different soccer teams. If we lose, is it my fault as the goalkeeper or is it team's fault for letting the goal go get to me so that I have to make a save? I'd say it's all of our faults, right? And it's the same in software. It really is a team sport. And any other that you've heard? I mean, you talk to a lot of developers and a lot of different people at conferences and attendants and everything else. No, I think we've covered it. And I think, I don't buy that it's hard because everything in this industry is hard, right? Nothing, and nothing's hard once you know how to do it. I think the, how do I get started doing it? I don't know, I don't have time or I don't know how to get started doing it or I don't understand how to integrate it into existing projects would be the one that I would have the hardest time scratching my head up. Yeah, and we can talk about that. More words, we'll just go really quickly. This kind of sums up everything we've said. So there's not a whole lot for me to talk about on this one. So for the last one, really, you do get increased agility and faster time to market. And this is the whole benefit of talking about DevOps and the whole movement towards DevOps is to reduce mean time to failure, mean time to market. It's to get on this more constant cadence of releases and release better software in more manageable pieces. Yes, again, going back to how do you eat an elephant, right? Yeah. One bite at a time. So let's go into Visual Studio and I've downloaded a couple open source Nougat packages and let me just bring up Nougat packages for the solution. So I'm using X unit and that's the one I typically use. Now there's some updates out there. I'm not going to update here on air. X unit is by James Newkirk and Brad Wilson and these guys were pivotal in N unit, which was it's still a very popular framework. But kind of like the reset Microsoft did with .NET Core where they said, well, we've got all this stuff we have to support because 10 years ago we thought it was a good idea and unit had some of those flaws as well. And what they've learned and what we've learned as a testing community is there's certain constructs that we shouldn't do like the expected exception at the test level. We'll talk about how to handle exceptions that are more accurate. So X unit works with .NET Core, works with the full .NET framework. It's a competitor to not a competitor because it's free and open source, but MS test is another popular one. And the techniques we're talking about work with MS test as well. Work with MS test. You don't have to be using X unit to do TDD, right? No, okay, good. Some of the terminology is different. We'll talk about that when we get into the code. Okay. To run X unit in Visual Studio you have to download the xunit.runner.visualstudio package. Now I have resharper installed on my machine. I'm not gonna use resharper to run the test because not all of your viewers have it, right? And then another framework that I have is MOQ. And you can ask a hundred people on the street how to pronounce it and you'll get a hundred different answers. So I usually just call it mock, but people also refer to as MOQ and it's a mocking framework and we'll talk about what that is when we get into the code. All right. So something that we did together when we were talking about this, let's just make a new class and we'll just call it foo. So this is going to be my test context, right? So tests are run in what's called a context. The way that all of the frameworks work is there is a context that has tests in there. So you're gonna write a test. I'm gonna write a test. Right now, okay. That is in the test project. Right. Okay. So xunit calls them facts. There's also theories which we'll talk about shortly. MS tests are just called a test. Okay. Right. That's just the calculator example. Did that make sense? Should we do that again? Yeah. Okay. Should return a sum. All right. So the TDD mantra, if you will, is red, green, refactor. So you write a test that's failing and then you write just enough code to get it green and then you refactor to clean up your code. Okay. So a failed build is also a failed test. So we'll just do this. We'll do var sut for system under test equals new, I can't use calculator. Yes, I can't because I can do this. The magic of TV, we probably won't see this happen or we will. All right. So I have this calculator class. Okay. I go to do a build. It's gonna fail. It's a failed test, right? Doesn't exist. So let's create the calculator and I'm gonna put it right here in the same class. This is bad juju. You don't want to have your, well you don't want to have any more than one item in a file anyway. Yeah, but you should get a light bulb that pops up asking if you want to move it to a different file. You know, like that? Wasn't a light bulb. Although that hammer is part of refactor. I mean, resharper? No. So resharper will. Where'd the hammer come from? The toolbox. I don't know. You're the Visual Studio toolbox guy. I know, but I would usually see a light bulb. Okay. All right. But let's not get caught up in what icons you have in the left. So there are. Okay, so you created a calculator class. I created a calculator. So there's three. Okay. So there's three steps to testing. Yep. Arrange, act, and assert. Okay. So I've arranged, now I'm gonna act. So let's just say var sum equals system under test dot add three comma one. Okay. Again, a failing test. It won't compile. So let's add that method. Nope. Let's make this a little better. Okay. And we're gonna call this add end one and add end. That's gotta be a last century term. Two. And well, I am from the last century. So. Would you have one of those HB calculators? TI 35, best calculator ever. All right. So now I've arranged, I've acted, and I wanna assert that the action was valid. Okay. Now one place where people get hung up, and again, this is academic program, programmatic, and then confused. So academic, you should only ever have one assertion. Always only have one action. If you have more than one action in unit test and something fails, well, which one failed, right? Goes back to single responsibility. Right, and you're not testing one thing. Right. I am more in the middle with the number of assertions. So the real hardcore TDD guys and gals will say you should only ever have one assertion. I'm like, you should have the right number of assertions. Whatever that is. But you don't wanna have so many that you're getting confused. Right. So we just say assert equal and we know that three plus one equals four. So we have our expected here, and the actual there. Okay. Okay. To run this, we go up to test, windows, test explorer. Nope, I have lots of tests in here already. Rebuild, so we see the test. Ooh, should return to some. There we go. In the middle. So let's run this particular test. I can right click and say run selected test. And it said come back as a failed test because it threw an exception not implemented. So we have to go fix that. Okay, so before we move on. Yes. I know what you're gonna ask. This is a lot of work for a really simple method. So at this point, I don't wanna understand. You haven't written any code. You've written a test that just asserts that you're gonna return a four. What is this gaining me? What have you literally accomplished? So what I've accomplished is in real life, nothing. Okay. Okay. Fair enough. For learning how to do the testing patterns, this is a good way to get started. Okay. Fair enough. So it's wax on, wax off. Yeah. Before you start learning the codis. Okay. Right. So I'm gonna write that muscle memory down. This is the same way that I do complicated methods as well. Okay. So for example, if I've got a crazy link statement in any framework, I'm gonna write a unit test to make sure I get back the right records. Yep. And then I start writing the code. Now I do not run the test, try and run it with the exception in there and everything else, right? There's something that Michael Feathers talks about is called the obvious implementation. All right. This is obvious. We shouldn't do this, right? We shouldn't do all these steps, but there is some advantage to doing it in this manner, even if you skip steps in your head. We talk about amount of code and that you'll write less code if you're doing test-driven development. It's because the goal is to write just enough code to make the test pass. Okay. If you write more code than that, you're gold plating. Right? You're putting additional things in there. We do it all the time as developers. Write enough code in the test or in the method? Well, in both. Okay. But let's just step out of the testing scenario and you pull a card off the board and it says should accept American Express as a method of payment. And as developers we go, well next they're gonna ask for MasterCard and Visa so I'm just gonna program that in. We're making an assumption. Yeah. We're adding additional code, but maybe they've got a deal with American Express that they will only ever accept American Express. So we've gold plated, we've weakened our code base and we've added features that will never be used. Okay. And it can cause bugs, right? So if we pull the card it says should accept American Express and we write a test to give it an American Express number and you can check that using the LUN 10 algorithm. So does that mean that you're treating the test as your spec? Acceptance, yes, it is a spec. Oh, okay, got it. It is the acceptance criteria to specification and when we look at M-spec, which stands for machine specifications, we call them specifications, you know, column tests. Okay, all right. All right. So again, this is a very trivial example, but one really good way to get better at writing unit tests is to go out to like Project Euler or something like that and find a problem and write it 10 times, right? Project Euler at E-U-Y-L-E-R. So it's just a whole bunch of simple problems that people will use as code coders. So you pick one of them. A Cota, it's a Cota. A Cota is, it comes from Marshall Arts. So when you study Marshall Arts, of course you learn how to punch and block and things like that, but then you put it together in what's called a Cota. It's a form. So I'm going to do a block, a punch, a step, a kick, right? I'm gonna do these things in order and I do them the same way each time. So it's not really applicable to code coders, but that gets me better at doing those individual items. With a code Cota, it comes from that same thing, but we don't want you doing it the same way each time. Take this problem, solve it, delete all your code. Take the exact same problem, solve it again, delete all your code. And if you do it with writing a unit test first, then you'll get better at unit testing. And the reason why we like using those types of problems is because when you get into domain-specific problems, our judgment gets clouded, right? I used to use banking examples when I talk about different patterns. Invariably, somebody in the audience works for a bank. I'm gonna come up and say, well, that's not how we do that. So now I have very silly examples that I use so we take that whole domain out of it, right? So this is how we practice to get better at it. So if I want to return the minimal amount of code to make this test pass, I would just code return four, right? Because I'm passing in a three and a one, and it's gonna return a four. So I can run the test again. We know it'll pass. So it's kind of silly to run it again. So are we now at green? We are now green. That's the minimal amount of code to get the test to pass. Correct. Okay. But there's a problem with this code. Well, I have 100% code coverage. I have probably very close to 0% use case coverage. Right. And this is where people get hung up a little bit. They look at code coverage as this magic metric. And it's just a flag. If you have 70% code coverage on some method, I'll probably have a discussion with you about why. If you got a good reason why, that's enough. Okay. But here I have 100% code coverage, but it's only good for three different options. Three, one, two, two, four, zero, right? So how do we fix this? Well, one way that we fix it is we change our fact to a theory. And this is like a row test in MSTester and unit. And this allows us to expand our use case coverage without rewriting the same code. So what we do here is we pass in different values that become parameters to our test. Okay. So I'm gonna refactor this test like this. And then I'm gonna add in these parameters. So add end one, add end two, and our sum. So then what we do is we replace our hard-coded values, refactoring along the way to make it a little more clear. So I screwed up a little bit, which is probably only because I'm doing live coding because I do this all day long for real life. We declare the theory to show that we're gonna have data coming. Actually, it has several different ways you can pass in data. Inline data is probably the easiest. So that was just to screw up on my part, being on camera, everything else. No worries. All right, so now I run this test, we're gonna go green, right? I haven't changed anything. Let's add another set of values and let's do this. And if we build this and go back to our test explorer, I should hide all these, here we go. I now have two tests in test explorer, even though I have only one set of code. So what the theory or the row test, or I don't remember the exact syntax for MS test, enables us to do is reuse code. Without that capability, I'd have to write a whole bunch of tests just to pass different values in. So I run both of these and guess what's gonna happen? You know what's gonna happen, right? One will pass, one will fail. Because, tell me again why one will fail? Because we are still just returning four. Okay. But we've expanded our use case coverage. Oh right, so one of the data's, one of the data's passed in, three, three, three. Okay. Right? Yep. So let's go into this method and do the right thing. Okay. We can run it again, but we know what's gonna pass. Right? Again, this is something I probably wouldn't, no, not probably, I definitely wouldn't do it this way in real life with something this simple, right? I'd still want to test in place, but I'd write the test, I'd write the code the right way the first time and be done with it. But when you are looking at something very, very complicated, this is a good way to build up. Okay. Right? You wouldn't have functions that are too complicated, but sometimes you're just stuck. You can't break down that elephant anymore. And you've got a rib and you just got to eat it, right? Whether it's a big hairy math problem or something else. There's another benefit that you get from this in that as developers, we are very good at testing the happy path. We know what should be entered in, we know how it's meant to be used. And when we test, we tend to test it that way. But users don't know that. My dad will just start typing, random stuff, right? So what happens if somebody puts in int dot max value and two, what's that going to equal? I don't really know. Let's find out. It would equal int dot max value plus two. Yes, but we're returning an int, right? Yeah. So this is how I get that rapid feedback loop. All right? Okay. Sorry, there was a, I have it set differently. Typically when the compiler in C sharp seven recognizes there'll be an overflow, it just kind of stops you and doesn't let you shoot your foot off. I have this project set up so that I can shoot my foot off. Okay. So if we take int dot max value plus three, we get some crazy negative number. Right. So what should we do? Well, it depends on the business case, but probably in my calculator, what I would do is I would check for an overflow or check for either of the add-ins being larger than a certain number, and handle it appropriately. So we've got something here just by adding one line of code with different values that might have made it all the way into production and then had it come all the way back and we'd have to fix it. Okay. Does that make sense? Yep. Okay. You want to talk? And again, the idea of writing the test first, which makes it more likely that you're going to write the test in the first place is that you're then more likely to think about edge cases that could break your code as opposed to writing the code, return add end one plus add end two. I don't need to think about that anymore. Of course, that's the right code, right? Of course, that's the right code. And then, oh, I should probably test that. I'll plug in a three and a one and expect four back. It's good to go. And when am I going to sit there and think about what are all the ways this could break? I'm not. It works. I'm going to move on to the next exciting thing to write. Yeah. Right? I'm just adding numbers. Right. So this is a way of training yourself when you're writing and when you're in the test first, because you know how to code. Right. The question is to train yourself how to write good tests. So that you wind up writing code that's more likely to work. Correct. Cool. And that's a difference between test-driven development and test-driven design. Yeah. So it's actually somewhat of a misleading name, test-driven development, or a name that doesn't jump out as something that, I mean, Agile, that sounds good. I want to be Agile. Who wants to be, you know, a laggard? Who wants to be sluggish? I want to be Agile, right? Who wants to write test-driven development? That doesn't make me want to do it. True. Until I understand how. And there's a slide I have in the conference version of this talk. I have a slide where it's big bold letters. It's not about the tests. Unfortunately, in C-Sharp and statically typed languages, because I know there's some dynamic in C-Sharp, but the only way we really have of driving out to design is through unit tests. Right. You know, another example, customer I was working with, they've got a really big, very slow legacy web app. NBC5, almost no tests in place, and they were having a problem with one particular. Well, it's not slow because it's NBC5, I hope. No. But.NET Core is a lot faster. True. So, and as I mentioned, it's a launch, right? We're removing any framework, six over any framework core, and we got significant performance improvements in just that. Anyway, plugging core because I'm a big fan. But there was one part of the website that was failing, and they weren't really sure why. And it's some younger developers, and they were working through launch the web app, log in, go through all these things to test this one block of code. This goes to your question about how do you introduce this into a bigger system where you don't have tests in place? Well, one of the things I did was I said, okay, where is this section of code? Hopefully, it's not dependent on system.web because you can't isolate that, but it wasn't, it was in a service. So I created a test, I called this service, and I wrote it to match the requirement that I had, and I could see right away, very quickly, that it was failing. And then so we tried to fix, and I ran the test, and it failed, and I tried to fix, right? And in about a span of an hour, it must have tried 150 different variations. And it was a problem with, actually it's the promenade of core that I need to report, but we ended up just then punting, going back to straight ADO, getting a connection, building a data reader, populating this particular record. And we did it, and like I said, in about an hour, when they could only test one or two iterations in that same time period, because it was so deep in the website, and you had to go through so many hoops to set up, right? So that's an example of introducing testing in the middle. Another way is, well, we've all seen these methods, 50,000 lines, probably named do it, right? With all these parameters. If you need to make a change in the middle of that, there's no confidence there, there's no courage. I'm afraid to make this change. And we mentioned this on the last show, what a lot of people have done, and I have done this in the past, is your clipboard inherit that method into a new one called do it two, right? Make the change. Do it right this time. Yeah, and you still have all this other code in there that now is not doing anything, you just double that code base. So write some tests. This is test eventual development. Based on your understanding of the system, don't make any changes to your code yet, and so all these tests will pass, and it's a boring job. That's another reason why test eventual development doesn't work, because nobody likes doing it, right? I'm just writing a bunch of tests, seemingly for the sake of writing tests. But once you have all these tests in place, now I can refactor. Let me pull this chunk of code out into a separate method and call it this way, do all my tests still pass, great, I haven't broken production. Now I'm gonna refactor this, I'm gonna introduce my change, actually I'm not gonna introduce my change yet, I'm gonna write another test now that's failing, because it'll only pass when I have this new feature, bug fix or whatever. And so what I've done is I've narrowed down the code base that I'm working with, and I have the confidence to do that, because I have tests in place. Does that make sense? Okay, let's talk about behavior-driven development. Let's talk about, I just have one question first, go back to the test. Absolutely. Where you, is there a way to feed in a number of these inline datas? There is, absolutely. And we'll put a link to, this is all in GitHub, so we'll link that up in the show notes. But if we want to feed in data, so we saw this method here, we just do inline data. Yep, okay, there we go. So you can pass in an I enumerable, and then you can generate random numbers, pull from a database, do whatever you want, sweet. Okay, cool. Whoever you want to read from a file, doesn't matter. Okay. And you'll notice that each object that gets returned matches these. Yep. Very, very powerful. Cool. All right. So machine specifications, it's again another open source project. And the syntax is a little confusing for some, because what we have is we basically have a whole bunch of delegates here, right? So establish is a keyword within the framework. Because is a keyword in the framework. It is a keyword. But what we have, we have our range. So I'm establishing the context. What do I have? I've got this service, and I create an instance of it. Typically in BDD, you call it the subject as opposed to the system under tests. Those are just naming conventions. It doesn't really matter what you use. Because is my action. Remember, I only ever have one action in a test. Here are my asserts. And again, I've got a couple of asserts in here. It's again, academically, people on the far academic side say you should only have one assert. In reality, you need to test what you need to test. Okay. Each of these run as an individual test in the test runner. So if we look at test explorer, we actually will have, let me run both of these. Now look at what Visual Studio has done for us, the test runner, but the Visual Studio. It's taken all of the underscores out. Yep. And it combines the class name when authenticating an admin user with the it. So technically, I should probably make it a lower case should. That's me being a Pascal case, you know, just cause I'm old school. But each of these come as an individual test. Cool. Now, if I didn't have any code in there, and if I could learn how to type, this is why we don't like to do live typing on stage because we don't screw up. Actually, you know what, let's use our other example. It should do the Kessel run in 12. So we do the Kessel run in 12 parsecs, but I don't have an implementation in there. This is how I'm defining my specification, but not having a test cause I don't have any code to test. So we define all these specifications. And then as we drill down, we get that bite size piece of elephant. We switch over to maybe use an X unit or MS test. And we test those and they pass and then we can start working our way up. So I use a combination of both, M spec to capture the behavior and then X unit or MS test to capture the actual implementation. Okay. Does that make sense? Yep. Okay. Last thing I wanna cover is mocking. And I don't mean making fun of me because I screwed up live typing, but replacing a dependency. Yes. So I talk about testing. A true unit test is really just testing a simple unit of work. Any dependencies need to be handed in. If I was writing a test to see what records I get back from the database, that's not a unit test. It's an integration test. Right. At the end of the day, to me, it's a test. I don't get really hung up on the vernacular, but the problem with testing the database is the database changes. Unless you've got some way to always make sure you've got the exact same records in the database. Right. Which I will show you how to do that. Is your test that a specific data source returns data, which can then be manipulated? Or is your test that if data comes in, it gets manipulated correctly? Two different things are going on. You're retrieving data from a database, which could work or could not work, and then you're manipulating it, which could work or not work. Right. So if your unit test actually goes out and calls the database, now you're testing two things at once. Exactly. Not to mention, the practical point is that you have to be connected. Right. You can't test this on an airplane or offline. And it slows down, right? Yeah. It's going to be significant. You have to wait. You have to wait. So what we have are mocks, fakes, and stubs. Mm-hmm. So a fake is I build another class that has the same interface as my iRepo, and we'll call it fooRepo or testRepo. And I hard code. If you call find, return this record. If you call find all, then you return these records. Mm-hmm. And that's okay. It works. But what if I want to start adding additional use cases to that? So now I'm stuck. Or I create a whole bunch of different objects. Now I'm cluttering my test code with classes. Mm-hmm. What's better are stubs and mocks. They're a little bit different. The difference is academic. We pretty much use the word mock to cover mocks and stubs. You can go out to Wikipedia and see the exact difference. We're just going to use mocks today. Okay. So what a mock does is replace, sorry, replace an interface. And mock, just to be clear, this is... This is... Would you get this from? This is in the MOQ framework that showed it to be guided. Yep, okay. There are several out there. MOQ is a very popular one. There are some commercial ones available as well. So the free open source ones can mock interfaces. The for pay ones can mock privates, actual instances, things like that. That's a much deeper conversation. Okay. The pay ones are better with legacy code because you probably don't have these interfaces already coded, but Greenfield, or as you refactor to an interface, as we talked in solid, you should always code to an interface. This will work fine. Okay. So I create an interface of my I repo. It's important to note that I am not testing the repo. I'm testing there's something that needs the repo, right? So I have a controller, could be an MVC controller, could be just whatever, and it needs a repo because it's going to get data. It's going to act on the data as you suggested. I then set up. What this does is on that proxy created on line 23, line 25 says, if this proxy is called, the find method is called on this proxy, passing in any integer is going to return that hard coded instance of customer on line 22. Okay. So then I have my new test controller that needs an instance of I repo to exist. So I pass it in, and then I call this is my acting on the data, right? So get customer, again, this is a very trivial just for teaching purposes. It really just shells out to the repo. Okay. And then I just got some silly asserts in here to show that I'm actually returning that customer. Now, if I want to have different use cases, then I could actually take these values, make this a theory, pass them in, and test all these different iterations without having to write any additional code. Right. So that's one of the beauties of using mocks. Okay. The other one is I can verify behavior. So I've set up that this method will be called with any integer. And when that happens, returns cost. So let's just look at the setup prior to the returns. This is actually putting into a list of expectations that that method will get called with that parameter. In this case, any parameter. Okay. So then I verify that that method was called passing in, in this case, a 12. If I change this to a 13, I will get an exception. Because it wasn't called with a 13, it was called with a 12. So why does this matter? Well, let's say I've got a method, I want to test this method. And maybe the method is a controller for a bunch of different command pattern methods. And it's calling all these commands out. And we want to make sure every single one of these gets called. Well, as long as those command methods come off of an interface, I can call that the broker method, set up the expectations that all these methods get called on these mock objects, and then verify that it didn't happen. So now I know my code is behaving the way it's supposed to behave. Does that make sense? Yep, yep. So really, tying it all together, there's not one tool to do this. You've got X unit, MS test, N unit, MB unit, all those different frameworks for straight, test driven development or design. MSPEC is a way to look at it differently where you start facing on the outside, working your way in, and then mocking, using, like in this case, MOQ, I'm replacing those dependencies so I'm isolating the system under test. If this was calling into the database for real, and I was on an airplane, this would fail. Yeah. Because I can't get to the database, but which failed? Was it database call or was it my code? Now I'm doing too many things in my action. Right. Cool. That's what I got. So this is, I think this has been a great overview of how you would get started doing this, why you should think about doing this. I love the way it moves your focus from just the actual code which is implementation to why you're writing the code in the first place, which is to match some design. And then at the same time, you're writing your tests, which is always a good thing. So you can think of it that way, right? Yes. Treating the tests as more of the spec and the requirement so you make sure you're literally writing the right code and then being able to test it at the same time seems like a great place to wind up. Right. So where would you go to learn more about this? Well, certainly, there's lots of books out there in Chempec, it's got a great book on it. XP covers unit testing in pretty good detail as well. Alpharol site and Linda both have courses on TDD and C-Sharp. That's what we were specifically talking about today, but this isn't limited to C-Sharp. Right. You know, there's frameworks for JavaScript, for Java, for lots of different languages. Okay, so we'll put up some pointers to where you can download some of the things you showed, as well as where you can start playing around with this. And you might be best off starting with a sample project, so you kind of get the sense of how to do it, and then you could look to start implementing it in existing projects. And again, it's piece by piece, right? If there's code that you wrote a long time ago that just works, that's not necessarily, you wouldn't necessarily go back and retrofit that. No, there's no real ROI on that, right? I mean, if code is working, it's in production, it's been tested by all those people using it, there's no real benefit to going and adding unit tests. As a matter of fact, I'd say it's a detractor because people don't like, it's boring, right? But let's say there's code you wrote three years ago, and it's working, but now the business has changed. Right. I mean, there's been a significant shakeup in the insurance industry with the Department of Labor regulations on fiduciary responsibility. And a lot of those insurance systems were probably written on mainframes, they've been working forever. And all these insurance companies have to go back and make major changes to code that the people who wrote it probably aren't even with the company anymore. So now I'm gonna start adding unit tests to cover my rear end, right? So I don't mess up something in production because I don't understand the side effects. Yep. Cool. All right. That was awesome. Great. Well, thanks for having me. All right. Hope you enjoyed that. And we'll see you on the next Visual Studio Toolbox. Have a great day.