Alright, so I'm doing my first "real" C# project. I'm chugging along, enjoying myself, nice little language I've got here, fairly easy to use, la ti da. Then I get to the point that I realize there's no simple way of setting up friend classes for unit testing.

Well. This is unfortunate.

I dig around and find that I can jump through some burning hoops and expose everything to another assembly with:

[assembly: InternalsVisibleTo("AssemblyName")]

But... this is a fairly minor in-house app that I've just going to a single binary and doesn't really warrant multiple assemblies. What other options - if any - does C# offer?

  • 2
    Unit tests aren't production code, they belong in another assembly. – StuperUser Mar 24 '11 at 17:56
  • 1
    This question is about C# unit testing in particular and not subjective, so belongs on StackOverflow. – StuperUser Mar 24 '11 at 17:59
  • @StuperUser A discussion of unit testing approaches can be sufficiently subjective. – Adam Lear Mar 24 '11 at 18:00
  • Very true, but the question is "What other options - if any - does C# offer?", which is objective. – StuperUser Mar 24 '11 at 18:25

What's the trouble with using public methods and classes? Your methods should be encapsulated appropriately and you should be writing tests against public and occasionally internal (which can be exposed to your testing assembly through InternalsVisibleTo) methods.

You shouldn't have to test private methods. Doing that means you're testing implementation details instead of a behaviour contract. Your tests shouldn't care how something gets done, just that it does get done. Doing it otherwise increases the chances of your tests being brittle and requiring extra maintenance when production code is modified.

A typical solution setup for C# is to have an assembly (or several) for your production code and an assembly (or several) for your tests. The assembly split for production code is generally decided based on functionality. For a small in-house app you'd be fine with just one. For tests, assemblies are sometimes split based on the kind of testing performed -- unit or integration.

I recommend following that pattern and placing your tests into a separate assembly that references your production code assembly. Make your classes/methods public as needed and test those features without worrying about the minutiae of their implementation in your tests.

| improve this answer | |
  • +1 For focusing on behaviour and trying to test private methods is a violation of knowing too much about the class - thus making it a brittle test. This is why I don't like MSTest and never recommend it. – Martin Blore Mar 24 '11 at 17:11
  • I'll try to keep this in mind.. I don't know, I've just had it drilled into my head that every function needs to be tested before integration testing. – trycatch Mar 24 '11 at 18:00
  • 2
    @trycatch Something in your code is calling your private methods. They will get executed that way. If they don't run through some public workflow, do you really need them? :) – Adam Lear Mar 24 '11 at 18:01
  • Sure, of course.. like I said, I've just had it beaten into me over and over to test every function individually before going to testing functions that call functions.. catch bugs at the lowest level and whatnot.. old habits or something to that effect. :P – trycatch Mar 24 '11 at 18:04

You could add an additional build configuration to Debug and Release called Test or UnitTest and define a compiler directive TEST or UNITTEST or whatever.

Then build your tests into the same assembly, even into the same classes if you really want to test privates. Then wrap the test class or test methods with #if TEST. That way if you build as Test the tests are included and your assembly is testable including privates. Build as Debug or Release and they're not included.

Maybe a little messy to include tests with your assembly and not perfect, but we've used it and it does work.

| improve this answer | |
  • Hadn't even considered that. That's a thought. And gets around multiple assemblies. I'll keep that in mind, thanks. – trycatch Mar 24 '11 at 16:08

None. There is no way to run unit tests without first building the tests into an assembly. That means one of two things:

  • Ship all your test code with your product. Not recommended--particularly if your tests do anything with a database.
  • Package the test code in a separate DLL that is used in house. Recommended.

Even Microsoft's unit test framework has this limitation.

| improve this answer | |
  • Well, this isn't being shipped, like I said, it's only a very small in-house app (and if it were bigger I'd be more interested in using InternalsVisibleTo) but I understand what you're saying. So.. I suppose this is the only way to go about it... I'm just having trouble figuring out how to keep this simple... I can create the testapp DLL project that everything is visible to.. then add all the classes to that project? – trycatch Mar 24 '11 at 15:27
  • Right. All test code goes in the test DLL, and all production code goes in the main assembly. Alternatively, if you had both your test classes and your production code in the main assembly, you would at least separate them by namespace. NUnit can find the tests inside any assembly and run them. With separate assemblies, the test assembly depends on the main assembly and the C# compiler will inject the assembly loading logic to make everything work for you. – Berin Loritsch Mar 24 '11 at 16:11
  • NOTE: In the event that you have a much larger project (with several DLLs and subprojects), it's perfectly OK to have just one unit test project for all of them instead of one per DLL. – Berin Loritsch Mar 24 '11 at 16:13
  • Okay, I'm kind of getting this now... So I create the main project, build target: binary. I create the test project, build target: library, references the binary? And then... all the private/protected things in the classes in the main project are going to have to be marked as internal and InternalsVisibleTo the test project? – trycatch Mar 24 '11 at 16:22
  • Pretty much. Now, I personally test private methods indirectly (i.e. by calling the methods that call them.) For protected methods I'll create a stub class that implements the base class so that I can have access to the protected members. That avoids the need to keep everything internal. – Berin Loritsch Mar 24 '11 at 16:44
[assembly: InternalsVisibleTo("AssemblyName")]

This does not expose everything to another assembly. This exposes members marked as internal.

public members are obviously exposed by default, however private and protected members are not.

Microsoft's recommendation for unit testing private methods it to use reflection or their automatically generated Private Accessors: How to: Test a Private Method

To get at a private method using reflection:

using System.Reflection

MyClass instance = new MyClass();
MethodInfo privateMethod = instance.GetType().GetMethod("PrivateMethod", BindingFlags.NonPublic | BindingFlags.Instance);

Then to run the method:

object returnvalue = privateMehtod.Invoke(instance, new object[] {any, parameters, the, method, needs});

I would only ever use this for testing. I would not use this to call a private method in normal code, because it's private for a reason.

| improve this answer | |
  • I tried this and.. I have no "Create Unit Tests" option... I like writing them myself though, but I didn't even consider the "internal" thing. Hm.. – trycatch Mar 24 '11 at 15:58
  • Admittedly, that article is old now. The same advice is given for 2008. If you can't generate unit tests, I'm guessing you're not using the inbuilt visual studio test tools (probably for the best ;). reflecting on a method in .Net 3.5 is trivial, I'll add an example. – Matt Ellen Mar 24 '11 at 16:11
  • You really shouldn't ever have to test a private method. If you find that you do, consider making it public or internal. Relying on private implementation details when writing unit tests leads to madness and pain. – Adam Lear Mar 24 '11 at 16:56

Your other options are putting the tests in the same assembly or making your classes more public.

Personally, I generally go with InternalsVisibleTo (I've just written a small project of Powershell Cmdlets with 20-odd tests and I've still done it that way). It does feel like a dubious thing to do when you first do it, but it becomes perfectly natural after a while. Fact is that you're being very specific about what the Internals will be Visible To.

| improve this answer | |

There is a better way than using friend classes. If you are using MSTest, right-click on a class name in the editor window.

On the contextual menu that appears select : Create Private Accessor. Then select your test project.

What this will do is create a class in your test project that will allow you to test the private members and methods of a class without making it public.

Here is how you use this accessor:

Foo foo = new Foo();

var shadowFoo = Foo_Accessor.AttachShadow(Foo);
shadowFoo._private ...

EDIT: Here is a link on how to use accessors: http://michaelflanakin.com/Weblog/tabid/142/articleType/ArticleView/articleId/1030/MSTest-Helper-Create-Private-Accessor.aspx.

Also see my comment for more information.

| improve this answer | |
  • I can't seem to get that to work.. plus I'm a fan of keeping my code cross-platform as much as possible and MSTest sounds rather.. MS specific... – trycatch Mar 24 '11 at 16:11
  • Your tests are going to be tied down to the testing framework you use anyway. If you choose to use MSTest I would use accessors as it allows to private members of classes without resorting to friend classes, internal assemblies, etc. To get it to work create a new test project in VS. Right-click on a non-test class. Select create acessor, then your test project. You should then see a new folder created in your test project with the accessor. You can then use the accessor in code. I added a link in my answer to a page explaining it. – Gilles Mar 24 '11 at 17:03

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.