Introduction
Have you ever worked on a piece code where you had a private method you wanted to Unit test, and ended up having a discussion about how to do it, whether to do it and if to do it at all?
- If your answer is Yes - Good. Keep reading.
- If your answer is No - You're either not using unit testing (bad), all your methods as public (probably bad) or you don't have friends/co-workers who are passionate about the issue (can be good or bad). In any case, keep reading.
Our code
For the sake of brevity, we'll use the following code. It's not complex, so you might wonder why I want to test it, but assume that this is a helper method, and that you're using funky linq with dynamic expressions and you're trying to make sure it works as you expect.
public class MainLib
{
private int AnswerTheFinalQuestion()
{
return 42;
}
}
There are different approaches and opinions about what to do next.
Case 1:
Some people say: Don't unit test private methods. Test only you're public interface/methods.
Answer 1:
Fair enough. In this case, you've got nothing to do. You can go back to Imgur or Reddit.
Assuming you care about your code, another option is to add more testing to your public methods in order to cover different cases in your private methods. This is an option, even though not the greatest.
Case 2:
Some people say: Give your test some special permissions
Answer 2:
The first option is using the InternalsVisibleToAttribute Class, but I find it a bit vexing.
Another option is by using private accessors, but again, not my cup of tea.
A newer, shinnier and better option is using a PrivateObject. If you go this way, your test will look like this:
[TestClass]
public class UnitTest
{
MainLib library = new MainLib();
[TestMethod]
public void TestMethod_AnswerTheFinalQuestion_PrivateObject()
{
PrivateObject private_object = new PrivateObject(library);
var result_private = private_object.Invoke("AnswerTheFinalQuestion");
Assert.AreEqual(42, result_private);
Console.Out.WriteLine("Method returned: {0} using the PrivateObject", result_private);
}
}
This will allow you to access your private methods that you wouldn't be able to access otherwise
Case 3:
Some people say: Oh, just change the access modifier to public and be sure to change it back when you're done (they might suggest leaving a sticky note on your computer, or even leaving some comments on your code).
Answer 3:
If this is what you're dealing with, no sticky notes and no comments will save you. This will eventually break and it will come back to bite you in the *** when you least expect it.
A Solution:
Please note that this is "A Solution" and not "The Solution". For those who look for the silver bullet:
Step 1 - create a wrapper:
Create a wrapper method in your code:
public int DEBUG_AnswerTheFinalQuestion()
{
return AnswerTheFinalQuestion();
}
Note that the name is quite explicit about the use of this method. You could use UnitTestWrapper_NameHere or whatever you need in order to make it CLEAR that if you see this code being called in production, there's some majestic unicorn crying in a dungeon somewhere in the world, and you should raise an alert about this.
Step 2 - create your unit test:
Create a Unit test that uses this method:
[TestMethod]
public void TestMethod_AnswerTheFinalQuestion_Wrapper()
{
int result = library.DEBUG_AnswerTheFinalQuestion();
Console.Out.WriteLine("Method returned: {0}" , result);
Assert.AreEqual(42,result);
}
Note that I'm using the same naming conventions as I do in my code. It's obvious to anyone reading this that:
- This is a Test method
- It tests the AnswerTheFinalQuestion method
- It uses a wrapper, and not the real method
This will let you test your method happily , and if you trust yourself or others to delete the wrapper and the test when it's no longer needed, it might be enough. I HIGHLY suggest AGAINST this kind of optimistic thinking when it comes to code. Remember: Expect the best, plan for the worst, and prepare to be surprised.
Step 3 - use the debug preprocessor directive:
These might sound like big words, but it's rather simple. Add the following to your code:
#if DEBUG
public int DEBUG_AnswerTheFinalQuestion()
{
return AnswerTheFinalQuestion();
}
#endif
This will tell the compiler that if you're building your code in debug mode, this will be included, but when you're building it in release mode, it will be completely removed, and no traces of this method will exist.
The benifit of this is that you can wrap it in a #region at the bottom of your class, and not worry about exposing your privates in public :) . Here's a screenshot that show's you how it'll look in both build types:
Step 4 - Your unit test:
You can stop here, but if you do, when you'll build your code in release mode, your test cases will break, because they do not contain the wrapper methods. You could comment out tests, but you can just reapply the above solution to your tests as well, simply wrap them in the same #if DEBUG directive. Here's what will happen otherwise:
Points of Interest
There are many ways to skin a cat, and this is just one of them. The point is to be aware of the different options, and use the tool that is best fitted to the job at hand. I hope you've learned something useful.
If you've found this article helpful, please vote for the article, leave a message, and feel free to post back to this article
History
July 26, 2014