BrandGhost

Beginner's Guide to Testing Internal Classes in C#

You left your unit tests until the end of developing your feature... again! And now that you have to figure out some critical components to test, you've found that you have internal classes that you need your unit tests to see. However, if you make them public then others can see them! Let's see what we can do about that! The code for this video can be found here: https://github.com/ncosentino/DevLeader/tree/master/InternalsVisibleTo For more software engineering videos, check this out: https:...
View Transcript
look I get it you left your unit test to the very end of your development process and now you're running into some challenges but you're not alone a lot of more Junior programmers struggle with these two things which are going to be prioritizing writing unit tests and unit testable code and scoping things properly in this video I'm going to teach you how you can unblock yourself when you finally started to refactor your code to be unit testable which you should have prioritized in the beginning and you're now dealing with scoping challenges when you want to make sure that you're not exposing the things to other consumers of your library that they shouldn't be able to see but you want your tests to be able to see those things now before I switch over to visual studio just a reminder that I have started my Weekly Newsletter I will put a link in the description feel free to check that out for a five minute read every weekend on software engineering and c-sharp topics all right let's jump over to visual studio walk through an example and I'll show you how this works all right here in Visual Studio I'm going to walk us through a really contrived example that should help demonstrate in a really simple way how you can work through this challenge so in this particular case we're going to start with a class that we were trying to build and it ends up having a method called our private method that we want to look at how we can test properly now one of the challenges with how this code is set up and let's pretend it's a lot more complicated than what I have here I just left some comments to actually illustrate that I understand that this looks very very simple but if you use your imagination a little bit and assume that this is something very complex we want to dedicate some testing effort to one of the challenges is that when this is a private method we don't have a really straightforward way to go test this unless we're calling into things that are public on our class and that might mean that we don't have full control over how we want to go test this method so for example in this particular case when we look at our public method it is always passing in this constant value of 5. now that might mean that if we had something more complicated in this method here our private method we're actually going to be unable to test this properly with other values because the only thing in the normal code execution that gets passed in is five and if you're more advanced watching this yes of course there's some fancy things we can do to actually call into this we could use reflection and get a little bit crazy with that but we're going to take what I would recommend is sort of the next approach to making this more testable and if you keep these patterns and practices in mind as you're going through this the next time you're writing some code you actually might be able to kind of save yourself some time and do this earlier so I'm going to jump over to this step two file here this will be available on GitHub for you if you want to go step through it yourself and actually check it out so I wanted to leave all of this code in here intact for you to play with alright so in step two usually what I recommend people do to make their code a little bit more testable is that they actually pull out the method that they want to test and have more control over onto its own dedicated class this is in line with the single responsibility principle and I mean in this particular case the method that we're looking at is extremely simple but again if you use your imagination a little bit we can imagine that it's a little bit more complex and we want to dedicate the time to test so if I scroll down a little bit here you can see that I've actually pulled out this public class now that we have it still is kind of labeled our private method it still does the same work but I just wanted to illustrate that we would take this method out from the other class make it public put it on this class and now technically it would be testable inside of our test project and that's because our test project is able to see all of the public classes that come out of this Library but like the comment suggests here if this is something that we wanted to actually keep private to our library and not actually exposed to other consumers this is where we get a little bit stuck because now it's testable the test can see it but that means that everyone else can see it too so if you were creating a library that you wanted to have as a nuget package or if you were working in a company and the code that you were creating was going to be consumed by other teams if you didn't want them to see this class because you didn't want it to be part of the public API that's available this is actually no good because now people can see it it does mean that your test can see it too though so just to scroll up here to see how this kind of works now before we move on because it's changed a slight amount we are passing in that other class as a dependency that's all that we're doing here and then we're just calling the method on that dependency it's still using five just to kind of illustrate this hard-coded situation where you're not getting a lot of control if you were to test through this method but because it is public we can go test this class directly but this leads us to our next step that we could take so if you don't want to make this public and expose it to everyone there is another scoped keyword that we can use that actually allows us to make it so that it's almost public but not public to everyone all right so I've gone on to step three in our internals visible 2 project and just to explain what's going on here I've switched that public keyword now to internal internal is going to mean that it acts like it's public within the library that we're dealing with but not outside of that but that leads us to another problem that I'll explain in just a moment but I'm just going to mention too that I've also put this interface on here and it's just defining the same type of signature that we were looking at before and I'm going to combine these pieces to kind of explain why and how this is going to work to make it more testable and one quick note I didn't have to go use an interface to go make this happen but when we go to wrap this up I think the other concepts are going to show you that you could probably approach this in a couple of different ways but the interface was my preferred way to do it here alright so with internal like I mentioned it acts like it's public within the library that we're dealing with but that's actually going to make it problematic for us when we go to test it and just to prove it I'm going to jump over to the test that I wrote to be able to test this class if we look in the test it has a red squiggly underneath here and it's actually telling us if you were to hover over this that the class we are keeping internal is not accessible to this test project now it looks interesting because it's actually highlighting the syntax properly because I've added the reference and visual studio knows that that class exists and actually knows that it has this dependency method defined on it however it is telling us that it's restricted from us to be able to use and that's going to introduce us to the solution for today's problem so what we're going to do is look at the Cs proj file for internals visible to and I've left some comments here that kind of show us what we can do and if I expand this we have this item group that's actually going to Define an assembly attribute called internals visible2 so internals visible 2 is actually going to allow us to Define which other libraries can see things that are marked as internal in our particular case we're just going to give the name of the other project where our tests are so that's called internals visible2 dot unit tests and by doing this if I save it and I jump back to the test file now we can see that the red squiggly has gone away and it's now testable so I could stop right there and you have everything you need to go test this stuff but I did want to comment on a couple of other things just to give you some tools for your travels going forward with this first off I'm going to go back to the Cs proj file and we're going to talk about this for just a moment so something that is a little bit weird about this internals visible to that I wanted to touch on is that in my opinion it becomes a little bit interesting that a dependency that you're talking about is able to know about the consumers of it as a dependency now when we're talking about just tests to me that doesn't seem so bad right because if you're talking about being able to have a couple of different test projects or in this case just one test project really that's not so bad it's pretty confined to just a particular scenario however if you did find that you wanted to keep adding projects into here and they weren't tests that's where I would start to question is it really supposed to be internal did you actually design your classes properly maybe it should be public and you need to think about your object design a little bit differently so I just wanted to give you a little bit of a warning there that if you start to use this and you're starting to use it with a lot of different cases that aren't just tests or aren't really consistent with how you're using it you might want to rethink how your objects are designed the next part I wanted to touch on is just a pattern that I use in my own code base in particular I sometimes just have a DOT test project I might have a DOT test dot unit project and Dot test.functional now in other cases I might even expand this to be Beyond functional it hasn't really come up but if I wanted to do things like performance testing or something else I might actually have some other names here but almost always for the projects I'm adding in larger Solutions I'm actually adding these three things just in case I have some internals that I want to be able to go test and the difference aside from just having multiple of these is I've actually included this part here where the assembly name can automatically get put in in front of DOT test dot unit and then obviously for the other cases here the reason that I like doing this is that I can copy and paste this entire block when I'm adding it into projects and I probably should at this point make my own template so that when I'm adding new projects this automatically gets dropped in for me but this pattern works really well and it allows me to just expose things to my tests that are internal and finally before wrapping this up I just wanted to go back to the interface discussion so why did I end up using an interface here well I'm going to undo some of this code to show you what happens when I don't have an interface and then we can talk about some different ways to solve this problem so you'll notice that I have the eye dependency interface added on to this internal sealed class and then I have the interface defined here and if I scroll back up to the class that we were originally looking at I've just added an underscore step three here to allow it to compile and you can see but it's largely the same except I pass in the eye dependency instead of the con Cree class now let's talk about why that's important because this class is public and this is if we assume something we want to expose to the other assemblies that can see our assembly so it's not something we want to keep internal this will actually break if we don't have a way to pass information into the Constructor so to illustrate this like I said I'm going to get rid of the interface momentarily so let's pull this off that means we need to take it off of here and then if we go ahead and instead pass in this up here you'll notice that we have a red squiggly and it looks like everything else is correct but the reason that we have this red squiggly is because we have a public Constructor and this public Constructor isn't actually allowed to work with this internal class we are keeping internal it would mean that no one outside of this Library can actually call The Constructor because they can't even see the class we are keeping internal so in fact you might have actually known about the internals visible too but didn't know how to solve this problem either so the way that I recommend doing it and this is just because I like using interfaces to separate out my classes to make them unit testable and mockable especially when I have more complicated dependencies but there are different ways to do this one of which is we could actually make this internal and now this part works but we need a way to allow our callers or the people consuming our library to be able to create this class that might mean that we have some Factory that can create this and access the internal Constructor and that means we have this internal code path that throughout the entire way keeps things internal until it actually returns one of these instances that could totally work and you don't need to have interfaces you could get creative with dependency injection and then actually have that all get instantiated with something like Auto fact or some other dependency container framework however I like using interfaces to be able to accomplish this because it feels more clean to me and it's a pattern that I'm used to using again I primarily lean on interfaces for being able to mock things when they're a little bit more complicated but obviously in this scenario we're just multiplying two numbers together it's not really that big of a deal but I think you get the point all right so if we go back to our test we can see that everything looks good here if I go to the test Explorer I should be able to see our tests show up I should be able to go run all of the tests and by the way if you're not sure how to get this working when you're setting up your test projects no sweat I will link a video right above here so you can go watch how to get this set up with the right nougat packages for running X unit inside of Visual Studio but it looks like we have a green check mark it does look like if we multiply two and three together we do get six and if we go check that out boom multiplying number one and number two together in this case two and three should be six all right so in this video we looked at internals visible two and how you can add that to your CS proj file to be able to allow something like a test project to see internals from another assembly I did call out a couple of concerns that you might want to think through when you're doing this one of which is that you don't want to continue this pattern of internals visible to to all sorts of other projects if you find yourself doing that and it's not consistent like just test projects for example I actually can't even think of other scenarios that make a lot of sense that I would be like that feels good so if you find yourself doing that you might want to rethink having internal as the actual access modifier it's probably a bit of a code smell and something to reconsider the other thing that we looked at briefly is once you do that and you have internal as the scoping of your class you do want to consider how you're able to pass that in as a dependency to something that that you're being able to test it might not actually be a scenario like I showed you today so you might not have to worry about that but if you are refactoring to be able to test things and going that internal route you do want to think about do I need an interface for that can I use dependency injection do I use a factory or something else to be able to construct that but just something to think through because you might get stuck there if you had to go through a refactoring process like I Illustrated so that's all for today I hope you found that useful just remember internal is visible too and keep in mind you want to design your code to be unit testable along the way so you're not rewriting it to go write your tests at the end take care and we'll see you next time

Frequently Asked Questions

What is the main challenge when trying to test private methods in C#?

The main challenge is that private methods are not directly accessible from outside the class, which makes it difficult to test them independently. You often have to rely on public methods that call these private methods, which can limit your ability to control the test inputs and outputs.

How can I make internal classes accessible to my test project without exposing them publicly?

You can use the 'InternalsVisibleTo' attribute in your project file. This allows you to specify which other assemblies can access internal members of your assembly, enabling your test project to access internal classes without making them public.

Why should I consider using interfaces when designing my classes for testing?

Using interfaces can help decouple your classes and make them more flexible and easier to test. It allows you to create mock implementations for testing purposes, which can simplify the testing of complex dependencies and improve the overall design of your code.

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