BrandGhost

Writing C# Unit Tests with xUnit and Moq

In this video, I demonstrate how you can get set up with a project in Visual Studio to write unit tests. I walk through how you can use xUnit in conjunction with Moq to set up a pattern for adding unit tests for your code. The example is very simple but offers lots of room to build! Project on GitHub: https://github.com/ncosentino/ProjectXyz https://github.com/ncosentino/Macerus My Social: LinkedIn: https://www.linkedin.com/in/nickcosen... Blog: http://www.devleader.ca/ GitHub: https://github....
View Transcript
so I just tried streaming and already messed it up so I'm gonna try again yeah so I wanted to make a video in a stream here about trying to write some unit tests for a bit of code that I was referring to in a couple other videos I just made so I'm finally getting around from the the system design part of it to to writing tests on some of the classes I made in particular so the background here is I have a looot generator class for a role-playing game and I want to be able to to cover it with some unit tests so I want to follow up at another point maybe with another video or something where I talk about why I want to put you in a test here and not other types of coded tests but I just want to focus on writing unit tests for for this discussion here so we can see that I have a looot generator class and what's publicly exposed I don't want to write unit tests for is this generative method so I'm gonna kind of walk through the tools I use for for writing unit tests obviously this is in c-sharp and inside of Visual Studio and to start I mean the things that I really like using for writing my unit tests are X unit which is a unit testing framework and mock which allows you to it's spelled moq but it's - MOC MOC k out implementations of interfaces so what this lets you do is as you're calling code it lets you control the things that you're calling because they're mock implementations that you've set up so super super quick example is that if I had a mock past in when I call this drop a table repository when this method gets called I can actually set up a mock object that returns what I want when this method is called and the reason why this is really cool for unit tests is it lets you control the flow of the code that's being exercised so with that said the unit test is going to allow you to direct flow of logic throughout what you're exercising but because you're using mocked objects your your attending what the behavior of these called objects is going to be so that's just something to keep in mind just a quick counter example is that if I wrote all of my tests and assumed that get all drop tables could never throw an exception but in reality it's very likely that it could or able to throw an exception that would mean that I don't have sufficient test coverage on this method generate loot to ensure that it's safe to call this so I just wanted to kind of illustrate that but again I want to follow up with another video where a stream about you know different types of coded tests you can put in place to kind of cover that so to get started I have this class loot generator inside of this project which is ends with game objects items generation and you can see that over here it's highlighted in blue and I've already gone ahead and I've made a test project but what I would do is I would add a you know right-click add new project blah blah blah add it all in and the thing that's important here is that is your dependencies right so I guess Visual Studio already did that for me and and opened it up but I've added in the X unit nougat package and the mock nougat package another thing that's really important is it's I don't know when the X you have changed it but they made it such that they're their test runner inside a visual studio actually is a nougat package as well so if you're not using something else like like jetbrains with their test runner and you're using the built-in Digital Studio One you actually want to make sure that you have the the Visual Studio test runner for this and I seem to not have it add it in here but it's running somehow but usually if so whoops but usually what what you would end up doing is I think it's exeunt it visual by search exeunt Visual Studio runner oh yeah it is here oh I must have it for some other project and it's caused us to to get detected so I'm not exactly sure how that works so I assume it's only working right now because I have another test project that actually references this nougat package so it's able to discover tests properly but regardless I should go ahead and have it installed for this so let's do that this is the the joy of doing things live and unrehearsed so there we go have that installed and what that's gonna let you do is actually in your test Explorer you can see that I can actually detect the tests that are inside of my solution so that's really important otherwise you could go we go write all these tests and you're like great we have all this code to exercise other code but what do I do with it you can call it on the command line but I like working in Visual Studio and really being able to do something like right-click and then run tests so yeah so like you see this this context menu I can just literally right click and run test which is it's really nice when I'm developing stuff so what we're gonna go ahead and do I've pinned both these things up here because this is going to lead the class I'm testing specifically this method generate loot and this is my test class right so you need to make sure that your test project has a reference to the the the project that you want to write tests for so in here I absolutely made sure that I have what's the name of this game objects item generation so right so this one right here this project reference is what I need to make sure that I can see this class and I'm going to go write test for right so the way I like doing that is so I have my my my test class here and the way Exene it works is that when you right your test methods the constructor for your test class is called every before every test method is run so if you have two test methods and obviously want to construct for your class for each test method that constructor will get called right so if there's two test methods your constructor gets called twice so it's a really cool way to put common setup code and because every test I'm gonna run is going to be using a looot generator I can do the generator and let me just resolve this come on this is going to be the system under test so all my test methods are going to be exercising this another thing that I like doing is I'm gonna have a mock repository and the way the mock repository works is that if you're not familiar with mocking frameworks essentially what I'm allowed to do with it is let me just write it out before I lose my train of thought it's the thing that it's going to provide the the instances of the mock objects so let's start by with these two things so I'm gonna make a constructor because this is going to meet the setup code that must run before every test so I'm gonna start mock repository equals new mock posit Ori and I'm gonna give it a mock behavior of strict and I'll explain what that means so when you have a mock object so let's pretend let me actually just make one so I don't know sorry it's hard to type and talk at the same time it's something out of practice so if I have this mock object here right when you call mock out object it actually gives you the interface that you're mocking so the things that I can call on this are generated essentially it's it's the only method on on this interface right okay so when you're calling your system under test and it hits a line of code that has a mocked object if you use a loose mock it just it just runs like it runs nothing right and we'll give you that if there's a return value on the method it gives you the default of it so what that lets you do is kind of just ignore that things are getting called and in my opinion when you're writing unit tests the whole point of the unit test is that you're trying to prove that the lines of code you're expecting to run are running the way that you expect right so it when you're using loose mocks it makes it like you're literally saying I don't care that this line of code called but if that's the case why are you writing the unit test anyway right so I want to get into more detail that like a follow-up video but I kind of just want to show like this is how you might set up and walk through some test scenario so anyway we're gonna be dealing with strict because it forces us to actually set up the mock objects if you if your system under tests when you're running through your lines of code if it hits a mocked object that is not set up right so if you didn't set up the method on that mocked object your test will fail right away so it lets you know like hey you're hitting a line of code you didn't set this up properly or you just forgot to or someone made a code change this method needs to be set up on your mock otherwise we don't know what we're supposed to do right okay so get our mock repository set up this will make a little bit more sense in a second after I write this line so I'm gonna make a new loop generator it's not going to be a mocked object and the reason why is I need the real thing here however my loop generator takes in a whole bunch of things so it takes in a drop table repository it takes in a drop table generator facade handler facade naming things gets real crazy so these names get really ridiculous there's like over 80 projects on my solution so as you're trying to come up with names for different domains and stuff gets really challenging so please forgive me for really long or weird names I need an attribute filter and I need a random number generator because we're dealing with a looot generator and if there's no randomness to it it's no fun so what's the problem here my loot generator has these four dependencies that I don't have yet but that's okay because we're gonna mock them out and the reason we want to mock them out is I want to control the flow of code through my lead generator right again I'll follow up with another video on it but if I put in the real draw table repository implementation a real drop table handler generator Persaud implementation and real implementations for these other two things that's okay it's just not going to be a unit test and the reason why is that I'm actually exercising other parts of the system other dependencies by passing in the real things that's a functional test that's totally cool too it's just a different thing so I have these four things and I'm gonna set up mocks for them so the way I do that is I make a mock eye drop table repository drop table sorry this part I like taking the time upfront to do this because you'll see that if I'm writing multiple tests all of a sudden it this little set up time that you do once you know you don't have to pay down again which is really nice so a little bit of investment up front kind of sucks but whatever the other thing is when you're passing in these mocks you need to pass in the object for them why is that well it's because I can't spell there we go yeah the joys of writing code live so this needs a dot object as well these ones aren't made yet but they'll also need object the reason why is because mock itself is a class right so I've made a mock but this lute generator takes in the interface that we're trying to mock so we give it dot object which if I hover over you can see when the telus ends dot object on this instance is a drop table repository an eye drop table repository and similarly on this one the dot object is the interface I just realized I'm pointing at my screen so you can't see that I'm pointing at the interface name but yeah so you can see in the intellisense there so I need two more mocks like I said this parts a little bit boring but please bear with me and I need an eye attribute filter so this is going to be located somewhere else thank God for that functionality or else I wouldn't know where I put all my stuff okay so cool this is looking pretty good so what this will do is we have a constructor we don't have any tests yet but our constructor will get called and it's going to pass these things in but if you're paying attention you'll realize that I'm passing in a underscore drop table repository dot object here I've never actually made this anywhere right I've declared it I've never assigned an instance to it so if you're paying attention you'll know that that would have exploded if I tried to run anything and how we make them is we actually ask our mock repository to create what we're interested in and we give it the type oops that's not the spot it goes over here there we go so when you ask this the reason why we're doing it this way this is a so in my professional experience what I've learned is that if you if you want to make more consistent more concise tests I think setting this stuff up in your constructors really beneficial the reason why is you can ask your mark repository to make these things for you it forces them to be strict because you've declared it to be strict right on the repository the other thing that we'll get to after we write our first test is that we can actually verify all the calls all the mocked objects not individually but all at once because mock repository and I'll explain what it means but has this verify all method right and each one of the mocks that you make so drop a repository has a verify on it but instead of me trying to call verify on all the mocks I have set up if I just make them off of the repository I can just call verify all right on the repository and it will verify all my mocks that I made with it so really powerful and if you don't know what verify all does stay tuned because we'll get to it when we write our first test but yeah so over time I found that when you start to see enough duplicated code or you find that you're doing this pattern a lot you'll find little optimizations like this okay so all I did was I actually made all of those mocks put some separation there so we make a repository we actually instantiate all the mocks off of the repository so all of these are now made with strict behavior which is great so we want for our unit tests and then we make our system under test cool now let's go write our first test so the way this works is it's the xunit framework is going to look for test methods I like writing my test methods with the pattern that looks like this so the first part is the method that we're gonna be testing and I think I said oops I just want to look it up really quick it's called generate loop right so we put that right here the part in the middle is generally the scenario that we're interested in running and the part at the end is your expected result and this is just a way that I've been practicing for writing tests that makes them that can't speak that describes them well so if you were to see a test running and pass you can see like that's even someone reading this coke and say I know what this test is supposed to be doing and when you see it fail you can see what scenario is you know is not being covered properly now because it's breaking so that's a I think a good way to name things now you can fall in whatever pattern you like I think it's just important that you're describing what's actually being exercised so I don't know yet what these scenarios or result is going to be I just know that we're calling generate loot so just for the moment I'm gonna leave it looking kind of ugly like that but what exeunt wants to do is it's going to be looking for something called a fact okay so your fact is going to be the attribute you put on your test method that tells X unit this is a test method so when the test runner goes to run this it will look through this class which has to be marked as public okay so your test class has to be public it will go look for all of the methods labeled fact and when it goes to run this it will first start by running the constructor like I said so all this code will run then it will go run your fact and if there was another fact it would go run your constructor again and go run the next fact okay so that way anything that's actually shared like as a as a field like a variable inside your class if it's not static it actually is a new instance every fact that you get run so that's pretty that's pretty powerful so let's us reuse code without sharing the state which you don't want to be doing in your unit test okay so to really understand what we want to be testing here basically I need to see what parameters I can pass in right so it's what parameters I can pass and then we're gonna have a look at the code to see the different paths we can exercise so in here I have a generator context I can get past in like the constructor we don't actually have this reference so we'll do the same thing I'm gonna go make a mock so just before you do that if you if you knew that say you're only gonna have like one test that looks like this and we're so in my particular case loot generator as I mentioned so I just flipped over to the class it only has one public method on it if you had a class with a bunch of small public methods it might not really be bent like there's no benefit to declaring your input as a field up here because you're not really reusing another test for what we're about to do though because I had a brief look at what the class looks like we're gonna have several tests that will always use a generator context so that's why it makes sense to set it up once I generator context comes from here we got to do the same thing so we don't forget which means that we'll have it instantiated boom okay so this is cool and what I like to do is VAR result equals call the method so I set things up this way there's like this pattern in unit testing that people call that they have different names for but like it's like arrange oops I can't type properly today either act cert so you would do all of your and the other ways that you call it set up do the thing so just just explain it so the arrange act a certain means that you set up everything for your test at the start of your method usually your your act or you know the part that's actually doing or exercising the code is almost always a single line right you're just calling this method right you're exercising one thing on your system under test then you have a spot at the end that does checks okay so that's cool what we this is our act part I used to label the sections of my tests or the range act assert started to not do that because I think that if you're if you're finding the tests you're writing are so large in lines of code that you need to label those sections it might not be it might be an indicator that maybe you should break up your your coded the system under test maybe it's doing too much so to me I started to realize if I felt I needed to put these things in to label the sections maybe I have a different problem sometimes when you're dealing with like actual production code and you're coming through and you're like hey this code didn't have tests on and I'm gonna add some sometimes it makes sense that you have some really unwieldy code that you need to have a lot of like you know scary but maybe like hundreds of lines of setup it sucks but the other option is that you either write a functional test which you could totally do or you do the unit test you exercise your code path and then you have this bit of ugly setup and if you don't want that I mean you could go ahead and try and refactor it but if you don't have if you don't have functional tests over the system you're trying to refactor it can be pretty risky so I'm gonna put act here just to demonstrate we're gonna have an arranged part oops and assert okay so now that we have some of the framework for our test method setup we have to go figure out what are we actually in a test here so if we were to call this right like technically right now I could go run this test I'm hoping it's going to explode right and it should because some of these methods some of these classes are gonna have methods that get called assuming that the body of this generate loop method isn't empty so some of these should get called and because there's strict mocks and things set up something should explode oh no I bet you I bet you I put in that test runner project or nudie package and it blew up what this didn't fail crazy okay well let's go read the code and see why I didn't I didn't rehearse any of this right so that actually looks wrong to me well when we are in doubt we can go debug it so yeah literally didn't rehearse this I figured this would be an easy class and I'm already proving myself wrong here so we have this loot generator we're gonna call generate lead with this context right so I'm gonna step in oops I didn't step in oh I did step in I know what happened okay this is kind of funny the the the little thing that I didn't notice is that this is an ienumerable and this will yield out items from this loot generator so a little little lesson if you're not familiar with a enumerables an ienumerable that is a iterator or I guess yeah it's generating objects it is just a function pointer okay so all that happened and it's not obvious all that happened was I ran this line of code and it said okay generate loot that's a function and it doesn't actually X like call the function it just assigns the function to this variable which is kind of funny so in order for us to actually exercise the code inside of it we actually have to do something like force enumeration so we can call to array on this and that's kind of funny that happen and you'll see that all that I'm doing now that's different is I'm actually telling the code go enumerate the things that come out of this right and we're going to assign all the things that come out of this method to result so now when I run this I bet money that it's going to explode I'm not saying how much though so i right-clicked I ran it you can see over here it's doing the exploration it's gonna go run this test and it should be read and it is cool and kind of as expected well that's really ugly one sec it says there's a mock exception it says I dropped able repository get all drop tables invocation failed with mock behavior strict so that's what we want to see all indications on the mock must have a corresponding set up that's exactly right I didn't set that thing up I don't know what I just did there cuz that's gonna anyway sorry I collapse out a little bit too far so that's exactly right so if we go check the code out I'm gonna jump to it you can see the like the first line of code after this comment says go call get all drop tables on this drop table repository reference however as I said this is gonna be a mock for us so we need to set it up with what we want it to do so let's go do that and before we go do that I think it's a good opportunity I was saying I don't actually know what the testing area we want to run is right so when I'm analyzing some code to think about the tests I want to run I look at some of the the control flow right so I can see we have a while loop right so I can see there's a while loop I can see that there is a error checking scenario so if this method is called and we got a null back then we should explode that's actually expected behavior so it's it's it means that this hopefully is never know but if it is we have a good we have a good message that comes up that tells us what's going wrong instead of us guessing about a null reference exception so there's a loop on the outside there's this you know error-checking scenario and the other thing is we have a for each loop here so another loop inside the loop and then there is this check here with two different conditions one is one remaining is zero and one is when the generated count equals some maximum generate count so already I can see there's a few different scenarios we have two checks here because there's an end we have a check here and we have a another two checks here so yeah there's there's a couple different things that we can we can dive into before we get to any of those checks though we can do some setups right up here with on these two things so drop table repository get all drop tables an attribute filter filter so let's go look at what that looks like to go set these up so I said drop table repository how we set these things up is your call dot setup and it takes a a method here so this is how you would write it get all drop tables it doesn't take any parameters but it does return something and what does it return an ienumerable of drop tables very cool okay so this is an example of something I might actually not set up in my list of fields up top okay the reason why is I have a feeling that I might write tests I'm just jumping to the class now I might write tests because this all draw up tables actually no I take that back I'll explain why I would have not put it as a field if all drop tables which is going to be an enumerable is something that in this class that we were going to iterate over I thought that maybe one of these leaps did that I might want to prove out what it looks like when we have nothing and when we have multiple things to it over those are two good testing scenarios to exercise when you have a collection that you're a numerating over what's it look like when it's empty and what's it look like when there's multiple things the reason that's not the case though is all draw tables this variable is only ever used to get passed into this other thing if I jump back out here filter drop tables has a method call on it now that I'm hovering over this I'm making myself upset I just realize I have an extension method which makes testing fun but this filtered drop tables is going to be something where you might want to pull out the reason why and this is getting a little bit ahead of ourselves is is that this random or default is a extension method and what an extension method means is that you're actually it's just syntactic sugar to to have a method call so actually this method really looks like random or default and you pass in fills or draw up tables and then you pass in rammed a number generator so it makes it look like you are it makes it look like you're calling this method random or default on this but you're not it just looks like that way this is actually a parameter into this method but why that sucks for us right now is that I can't actually mock any of this out it means that my tests must actually set up all the things that are inside of here and I'm about to jump into it I'm a little nervous because I don't know what's inside it's not too scary but you can see that I do have a for each loop that's going to iterate over things okay so that goes back to my scenario of what happens when there's nothing and what happens when there's multiple things I'm gonna take a shortcut for this I'm actually only going to put one thing into that collection and that way yeah that way we can we can do the the multiple tests a scenario I don't want to go through the the setup in this video of doing multiple things some more than one thing but when we want to test this scenario having no drop tables that's where we're gonna have some code Rees so kind of jumping around a little bit but that should hopefully help solve I can actually make this field like I was talking about where I have a I ienumerable of is it I drop table so I'm gonna call unfiltered drop tables I need to instantiate it again the reason why I'm doing it here is that this when I go to make other tests so there's more of these that are coming they're all going to need this right I'm just saving lines of code doing common setup in one spot if I change the logical flow of what happens pardon me inside this loop generator I only need to change it in one spot all this setup so should be okay so I want to return the unfiltered drop tables object cool that was with a one setup line jumping back to the class now all that work just to get this line here but now we're gonna go set up this guy and it's gonna look very similar so attribute filter filter takes in the drop table we just returned and the generator contacts so attribute filter oops not object sorry dot set up and we're calling filter and we're passing in the unfiltered drop tables on object and this generator context but what does it return right so that's where I'm gonna take this line this is potentially going to be different in our different tests so I'm going to set it up inside the test and it's going to be called filter drop tables and what I'm gonna do that's a little bit different is I said that I wanted to pass in actually you know what let's go with the error case okay let's go with the error case so that we can jump into here we can even go sooner okay so just I'll explain my train of thought here when I'm writing out these unit tests what I like to do is find the earliest return points or error cases that I can hit and what I can end up doing if that's the case is that I have one test that gets me let's just pretend I could get down to here with one test and then it's done the next test I go to write that wants to go a little bit further I can reuse a lot of that code and just change a parameter change a set up and then all of a sudden I get a little bit further the next test I go right I get a little bit further so I keep building on top of these tests so if I backtrack because I was getting ahead of myself a little bit too excited the quickest way for me to return out of here is if this conditions not met right so set up set up this needs to be set up too right because it's going to be a property on a mocked object and that way if I can make sure that this conditions not met we jump all the way to the end right so what that means is we can actually test the scenario that when this conditions not met we should expect nothing to come out right it never yielded anything so again if this condition is not met from the start we will never yield anything and that's a real scenario and we can assert that the number of game objects that comes out of this is empty so let's actually do that so I'm not gonna make a mock for this enumerables get really weird to mock out because you actually have to set up the the sort of built-in methods that enumerate it's kind of ugly but I'm gonna set up this scenario where I'm just doing there's no draw tables and you can see I didn't have to put dot object on here because it's not a mock so whatever there's no draw tables to come out but the thing that I need here is I need to set up this generated count and then I use instead of this generated count as well so that means we have this oops know what generator context dot setup we minimum and maximum rate I'm just gonna type in some numbers here because I haven't actually read the code yet to understand what I need I'm just making sure that I can get these put in place so I said I need to set these two things up but we want to make sure again I just jump back to the class I want to make sure that I can make this return right away so this condition says that remaining has to be greater than zero remaining to start is going to be what my minimum is and the generated count which is zero to start needs to be less than the max so really if I make if I make minimum generated count if I make that a negative number that's already gonna make it so that this boolean logic its short-circuited right if remaining is is assigned to the minimum generated count if I make this part negative one that means this becomes negative 1 negative 1 is not greater than 0 and we'll leave that seems I'm just I'm now that I'm saying that I allowed it seems like my logic is kind of flawed inside of here but anyway okay so let's do that because let's prove it basically I was saying if I make that zero that's the minimum generate account minimum generate account 0 is not greater than zero I think I'm just thinking about this I think this is actually a bug in my code but that's cool because we're writing tests for it and if it is a bug we can just go back and change the test to assert the the real thing that we want so basically what I was saying is we have this set up now this first line we have this set up oops we have this set up and I'm gonna I actually am leaving a bug in the test that I'm gonna see if we can catch but I'm gonna keep going and I'll try to explain it so that should let us return which means in theory that this result should be an empty set of game objects so that's where we can get into our third part here so assert empty thank you exiting it and we can say result oops the exact wrong thing from intellisense well that's pretty cool the other thing and I mention it near the beginning I was mentioning this verify all thing so mock repository verify all I like doing this at the end of all my tests there's a couple reasons why one is it makes sure like what it's doing is saying if you called set up on something make sure it's actually called so when you're using strict mocks and verify all it's kind of like coming at your tests from two directions so if you had loose mocks you're saying I don't care if this gets called and if you have strict mocks that's great because if they get called they must be set up so really good but in terms of test maintenance test readability and act we following your logical flow in your test if you're not verifying your setups you can have tons of extra setups cons right there's nothing stopping me from saying like okay I had get all drop tables like I think this one actually had another method on it so you know I might be able to do get like I can go set this up too I go set up all these extra things now my test scenario is super bloated super bloated hard to read and someone's coming by and they're like well this test is failing I don't know why is it this setup method like it's really hard to navigate so having this verify all call on here really helps keep your test focus on what the pardon so I know I know this test is gonna fail we run it because of a little thing that I kind of glazed over on purpose now I'm hoping it's gonna fail for the reason I think it might fail for another reason but I think I think we should jump into it so I'm gonna put a breakpoint here so we can check the result before we even assert I think that's important and I'm gonna put a breakpoint inside here so we can actually step through our test right the setup stuff happens pretty quick and it's just because none of the code inside here actually executes until the the mocks are being called so I'm gonna debug this test again context menu rate on the the method name which is which is awesome and we should hit up breakpoints in some day come on Visual Studio there we go okay so wait a little longer no pressure okay so we can step through our code just to prove this drop table repository is a mock right you can I'm pointing at my screen again so I'm helping yeah you can see in the hover over information there it's a mock I drop table repository f10 over it cool this variable is another Mach okay so we just proved that as expected we needed to set up this Mach 2 for this method to get called the verify all call at the end proves to us that this was in fact called now I'm gonna call filter drop tables sorry call the method to actually assign filter drop tables again it's a mock we set up right we set this one up and it's supposed to return an empty collection of drop tables I'm gonna jump back to the chest just to prove it to you but that's where we said this is an empty collection we're gonna return it here cool now we're gonna go down a line because that's just an assignment and down one more because this is also supposed to be a mock right generator contacts is a mock the minimum generated count we said we're gonna make it zero and the reason why is that we're trying to make this loop end right away so remaining is zero if I hover over this it's not true it says false which means we should leave here right away boom yes we do okay now very cool that we've got to the assert part of our test it means that what we set up for our test scenario didn't explode that's good news means we've set up all the mocks that we needed to also good news so let's check the result empty which is what we said should happen right now we have one more line of code and I was saying I'm pretty confident this test is going to fail so let's see because if I run this one line passes as expected because it's empty and I think this verify all call is going to fail moment of truth boom it failed verification why and it tells you it says on the generator context this mock failed verification due to the following we never actually call maximum generate count says the setup was not matched so you can see I set it up right here right I set it up this is not telling me that it wasn't set up it's saying the inverse is saying you did set this up but no one's actually using this this code should not be here because it's not in doing anything and that might make you pause and say well why like didn't we say didn't we say that that condition was right here right on this while loop aren't we asking for maximum generate count and sure we are but this remaining part was causing a short circuit in the boolean logic this was already false it never had to go evaluate the right hand side of this I kind of caught that as I was as I was writing it so I wanted to leave it in here's a little sort of Easter Egg that we can talk about to demonstrate how verify all work so I'm kind of glad something worked out like that so does that I can't really ask you if that made sense cuz you're not gonna be able to respond but yeah I think that's the point I was trying to make is that we set this thing up right it never actually got called so verify all for us is telling us you have extra code in here that's not getting set up sorry that was set up and not actually being used okay so the way to fix that it's just like that now I'm gonna go run this I'm not going to debug it so we're not gonna step through I'm gonna run it and we should see a green checkmark just gotta wait for Visual Studio when you're running single tests like this sometimes it can be a bit of a slowdown but when you have a suite of them it's nice and you can see green checkmark super exciting best day ever okay next up comes now we have a test working what do we actually test what was the condition right so we tested generate loot the condition was that is minimum generate count is zero and what do we expect to get back nothing you can see this is kind of cool in the test runner to actually change the name of the test so that's awesome this is this is we just made a unit test on this method for one one scenario now if you want to be picky and you want to test the other branch in here this is what I was saying with trying to make things terminate the method early because what you could do I know people say all copying pasting code don't do that we're about to do it so we're gonna go test another scenario here and I want to test that the maximum generate count is going to cause this to to blow up and sorry not blow up return early but that means that we need to meet this condition so that we can go test this condition and have it be false so we need to have minimum generate count greater than zero and maximum generate count less or equal to zero right that's the inverse of this condition so minim generate count greater than zero I'm just gonna copy paste it because I'm lazy not a good reason but and then I said maximum generate count on this side right because this this part of the condition will pass which means it's gonna go check this other side and it needs to be true for it to go inside the loop so we need to make it false to make this false we need maximum generate count to be greater than or equal to zero and one is certainly like that I guess I guess zero also works so let's just leave it at zero so the difference in this they look very similar right these two tests well what we're actually doing is checking the other condition in the loop because it's its expected behavior if this the whole point of the unit test is is kind of saying like you wrote this code right you wrote this code is it doing what you think it should be doing right is your logical flow actually as you expect so lets us get really granular and test the inner workings of our class sometimes people will argue like it makes an again I said this at the beginning I want to follow up with a video explaining this a little bit more but people will say well that's pretty scary like if I go touch anything in here it's gonna break tests and I say absolutely yes that's the point of the unit test it will break them but what shouldn't happen is that all of your unit tests break and they're impossible to maintain and it's awful and it sucks and you don't want to have them what should actually happen is that either a common setup that's shared amongst your test fails right because all of them required this code path to get hit so you can put a common sock in place or you have a couple of tests that we're exercising that part that changed that need to be updated and now they reflect the new expected flow so yes they're brittle but to fix them should be almost no effort the benefit to the unit test for me coming from working on teams that were primarily software engineers and not testers actually directly working on the products for us it was extremely beneficial to have coded tests specifically unit tests we were able to prove time and time again that when we had escapes that we could catch in telemetry we could look at stack traces and be like you know what like it's in this class it's this line of code cool we go look and we'd say oh we put unit tests on everything in this class except this spot right and we could just go add the correct it add the unit test so maybe we're missing a null check we could go right the unit test now that proves that we're doing a null check and that what behavior is expected there anyway it was a bit of a tangent I'm bad at that so we we basically just added another set up and changed one of the other set up so you can see very similar but we're building upon it so we need a name for it maximum generate count zero okay so again this verify all cut a call for us we've added another set up before it fails for us right but now it should pass because this is set up in a different way so verify all is gonna prove that for us I'm gonna go ahead and run both these tests and hopefully I did it right no pressure come on yeah cool okay so there we go this might be a good stopping point for this part in the video it wasn't too exciting because we didn't get into actually generating some of these things but just based on how much time I spent it might be a good stopping point but I guess just to circle back on what things we covered basically adding your test project making sure that your test project has a reference to the project with the classes you want to test very important add new get packages for mock which I'm highlighting up here add your new get packages for X unit making sure that you have an X unit visual studio test runner if you're if you're not using another sort of test runner like like something from JetBrains let's say then we made our test class that has to be public right very important or else XE and it can't discover it we talked about putting common setup use instance variables like so fields and setting them up in the constructor right so they can get reused you can see that this stuff had to get set up again for each of these tests so very important for for code reuse and saving lines of code we talked about strict so when you're writing in a test true unit tests you want strict so that you know that you're calling the things you expect and you're not getting some weird behavior where someone's just calling methods and they're doing nothing it's you're really not doing yourself a favor by having loose in here it would be happy to hear people have different opinions on that I would love to hear different perspectives on it to be honest that's my understanding so far we create these mocks pass them in to our system under test and then we were able to make to two test methods and they're labeled with fact again very important that X unit needs to see these fact labels to attribute story to to know that these are test methods then what we did was you talked about arrange act and assert so we did all of our setup under a range where we called set up on our mocked objects we called we called the the method under test under this little ax comment here just not that it's related to these unit tests but just a reminder we have an ienumerable that yields back things if you wanted to actually execute you need to enumerate it so I had to put two array here and the final part was just that we had an assert for our expected return value and we called verify all to make sure that we didn't have extra setups that didn't need to occur so yeah I could follow up with another video on trying to get some of these other parts tested I think a really interesting one would be catching exceptions another one would be about doing extension methods and how you can handle test that and then potentially another follow-up really explaining like why you might want functional tests versus coated coated unit tests so that should wrap it up hopefully that was useful yeah thanks

Frequently Asked Questions

What tools do you recommend for writing unit tests in C#?

I recommend using xUnit as the unit testing framework and Moq for mocking dependencies. These tools help streamline the testing process and allow for better control over the code being tested.

Why is it important to use strict mocks in unit tests?

Using strict mocks is important because they force you to set up all the expected method calls on your mocks. This ensures that your tests are verifying the correct behavior and not just passing because the mocked methods are ignored.

How do I structure my unit tests using xUnit?

I like to structure my unit tests using the Arrange, Act, Assert pattern. In the Arrange phase, I set up my mocks and any necessary data. The Act phase is where I call the method under test, and in the Assert phase, I check the results to ensure they match the expected outcomes.

These FAQs were generated by AI from the video transcript.
An error has occurred. This application may no longer respond until reloaded. Reload