BrandGhost

Easier Unit Tests in C# dotnet With Composition

In this video, we walk through an example of how leveraging composition in your code can dramatically improve your ability to write unit tests. Examples are in C# dotnet. Want the source for this video? Check it out here: https://github.com/ncosentino/DevLeader/blob/master/RefactoringCompositionDITesting/RefactoringCompositionDITesting.UrlParsingUnitTests/UrlParsingUnitTests.cs For more videos on programming with detailed examples, check this out: https://www.youtube.com/playlist?list=PLzATctV...
View Transcript
okay I realize this makes lots of programmers nervous but today we're going to talk about unit tests in particular I'm going to walk through a scenario that lends itself really well to unit testing from my experience and I'll show you how I set up code so that I can unit test effectively for this type of thing to make this go smoothly I'm going to talk about why composition is really important for this so if you haven't watched my other videos on composition or you're not familiar with the topic I highly recommend you go look at those first and then come right back here to continue this video okay let's jump into the code alright so the example code that I want to look at today to discuss unit testing is actually this block of code right here the purpose of this code is that it's supposed to be able to take some HTML content and actually use regular Expressions to pull out all of the href tags to get URLs from the HTML content from there it adds them to a list of URLs and I want to make sure that this regular expression is actually working as expected remember I talked about composition being really important for us to be able to unit test something like this so if you look at what this code is inside of currently it's in this really big method that has all of this other stuff going on I'm just going to zoom out to show you and you'll see that it's doing some other URL manipulation at the top it's going to be actually downloading something from the Internet it's going to be writing a file out to disk it does all of these other things but our URL extraction is right in the middle of this method so let's talk about why that makes it really difficult for us to unit test this properly if I jump over to my other project that I have that's just made for unit tests I just want to call out that I'm going to be using X unit here but the concepts that I'm going to talk about apply to other testing Frameworks like n unit or anything else you want to use alright so in X unit we can actually go call our Constructor to set up our system under test which is going to be awesome URL saver and every time we go run a test it will call this Constructor first some other housekeeping is that just because I'm using X unit our individual tests are marked with this fact attribute and I like to use a style of naming that looks something like the method name that we're going to be calling on our system a little short description about the scenario so setting up particular variables and then the last part of the naming convention is that I have something about the expectation that we're going to be looking at now this awesome URL saver is coming from our project that has no refactoring for composition so why this becomes really challenging for us to go unit test the URL parsing of this class is that I can't control the specific parts of this method so what does that mean so the only method that this class awesome URL saver has is save URLs async and the only things that I'm able to control are the actual URL and the actual file output I'll make a quick adjustment to the test just because this is asynchronous but if we look at what we're able to do here all that we can do is call this method and actually exercise the entire thing thing so why is that a problem for us well generally when we talk about unit tests and of course if you go read about different people's opinions and definitions of these words people are going to be very pedantic and I don't mean to stir the pot and Trigger people on the different particular phrasing that I'm using but in general with a unit test we do not want our code to be modifying or touching the external environment if you consider what this entire method is doing because we have to call the entire thing it is going to take a URL to download HTML from and it is taking a file path to go output content to if I jump back over to that method that means if we have a close look it will in fact go out to the internet to go download HTML this part here again is what we actually want to test and then the last part of this method which we have no choice but to exercise is actually going to write content to the disk so back in our test file one could argue that we could absolutely Go pass in a URL and a file path and then to go actually validate what's happening we could certainly go open the output file path and check for the URLs that we were interested in this is 100 a valid way that you could go test this if you wanted to however coming back to unit tests I don't want to write a test for this scenario that's going to access the external environment and that's going to include reaching out to the internet and also writing files to disk if you're wondering why that matters you might want to consider if you have something on a build system or something else that isn't guaranteed to have a clean environment to work with or isn't guaranteed to have an internet connection then all of a sudden these tests won't work the other thing to consider is that for unit tests we want them to go extremely fast so if you're actually connecting to the internet and actually doing i o on your computer instead of taking one two or a sub millisecond runtime you might actually be experiencing tests that take tens or hundreds of milliseconds to run again depending on the scope of what you're doing this might not matter at all but for us we're going to be focusing on unit tests that means that this approach for us is just not going to work we do want composition I have another example of this same awesome URL saver refactored to have composition in mind as a side note when we talk about composition you might notice that when we go to instantiate a single class our awesome URL saver that we have to go pass in all of these individual dependencies if you state right to the end of this video I'll show you a technique that you can use along with the nuget package that I love to use that will help solve this problem for you this will help prevent every time you want a new instance of a URL saver that you have to go instantiate all of the dependencies along with it and in fact you can see that I actually have this HTML URL extractor class and if I jump to that definition you can see that I have exactly the code that we want to test here so composition in this case is going to mean that if we just wanted to exercise this code now that this is pulled into a dedicated class with a single responsibility ability I can actually go write a unit test a lot easier let's head back to our unit testing class and actually change what we're going to try and test here so back in our test file we're actually going to instead use the HTML URL extractor instead of the awesome URL saber if we look at our setup now we're just instantiating a new HTML URL extractor in the Constructor and then when we look at our test method all that we have to do is call build URL list from HTML and like we saw if I jump over to that it's just the code that we're interested in exercising so this is initially why I wanted to show you why composition is very valuable for us to be able to unit test this stuff truly if we look back at the test method what we're able to do now is consider that the inputs we want to provide to this method against the outputs that we expect we could go ahead and write a very simple test here and actually try to create some HTML using this really awesome triple quote syntax now that we have in Dot at seven and then go ahead and create this href and see what we actually expect when we try to go run build URL list from HTML I'm placing a breakpoint here so we can go right click on this and debug the test and see what actually comes back from our URLs the text will be incredibly small but I can see that URLs has a count of one and actually has this text right here so this one worked exactly as I expected in this case we can go ahead and write one assertion here in this case using X unit we can use assert.single and check the URLs that we get back this will actually confirm that we have one item only inside of this URLs collection we can go ahead and add one more assertion which is actually checking that single URL to ensure it's exactly as we expected from our HTML if I run this again and actually step over the assertions we can see that both assertions pass and that means that we have one very simple test case that actually proves part of our URL parsing Works after we have our single test pleaded there's actually a bunch of different directions that we could go to go create more tests generally what I would likely do is probably literally copy and paste this test and then try to create more scenarios that I'd like to validate here after creating several of these tests I would probably want to take advantage of something in X unit that's called a theory and this would actually allow us to reuse the body of the test and pass in parameters here and this way I would be able to control the exact HTML I want to pass in and then the collection of URLs that I might want to assert on for this video though I just wanted to show that when I go look at my website I actually have some HTML that I've just pasted in here that doesn't actually get picked up by our parser we could actually go walk through how we might go test this by copying and pasting the test from above and changing the name to something like build URL list from HTML which is the method that I want to test given this next part that says no href quotes Because the actual scenario that I want to look at is when this href parameter does not not have any quotes I can also add in at the end here that I expect a single URL I've gone ahead and updated the input that we want to be looking at and then added the exact URL that we want to assert is in the collection of resulting URLs if I go run this test now by right clicking and debugging on it we can see that this test fails and it says the collection was expected to contain a single element but it was empty awesome so now we're in a state where we can actually go iterate because we have one test that will prove that we have this working scenario from before and we actually now have a failing test case that we can go try to improve if I jump to this original code which is actually the part that we were trying to test I can go now manipulate this regular expression to try and ensure that I can get both tests to pass and just a reminder that if we were not using composition we wouldn't have this luxury of just being able to exercise this one piece of code in fact we would have to go over back to the original code and we'd have to go exercise this entire method just to be able to go will work with this part right here that's highlighted so in this video we got to look at code that was refactored to take advantage of composition when we compared the original code and how testable it was for that one specific part where we wanted to parse URLs we could see that it was a lot more cumbersome versus the actual refactored code that took advantage of composition now I tried not to bore you by adding a million different test cases and walking through how it would go tweak that regular expression but I hope that demonstrated the point that you could actually have multiple tests set up go change the regular expression or whatever code you're working with and then actually be in a position because you have composed code that you can actually go exercise just that code that you're interested in and on that note of composition thanks for staying right to the end because if you watch this video right here you'll see something that you can use that will make your life with composition a lot easier

Frequently Asked Questions

Why is composition important for unit testing in C#?

Composition is crucial for unit testing because it allows me to isolate specific functionalities within my code. By breaking down larger methods into smaller, single-responsibility classes, I can test each piece independently without the interference of external dependencies like network calls or file I/O.

What challenges arise when trying to unit test code that isn't composed well?

When my code isn't composed well, I face challenges like having to test entire methods that interact with external systems, which can lead to flaky tests that depend on network availability or file system state. This makes my tests slower and less reliable, as they can fail for reasons unrelated to the actual logic I'm trying to validate.

How can I improve my unit tests if I have a large method with multiple responsibilities?

To improve my unit tests, I can refactor the large method into smaller, focused classes that each handle a specific task. This way, I can create unit tests for these individual classes, allowing me to test them in isolation and ensuring that my tests run quickly and reliably.

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