Click here to Skip to main content
15,881,248 members
Articles / DevOps / Load Testing
Tip/Trick

Safely expose your methods for Unit Testing

Rate me:
Please Sign up or sign in to vote.
4.80/5 (3 votes)
26 Jul 2014CPOL4 min read 16K   24   6   4
How to Unit Test private methods using wrappers and preproccesor directives

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.

C#
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:

C#
[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:

Image 1

Step 1 - create a wrapper:

Create a wrapper method in your code:

C++
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:

C#
[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:

  1. This is a Test method
  2. It tests the AnswerTheFinalQuestion method
  3. 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:

C#
#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:

Image 2

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:

Image 3

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 Smile | :)

History

July 26, 2014
  • Initial release

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer
Australia Australia
Coding since I Remember myself ... went through Basic on Commodore 64 to C# on a 24 cores AMD cpu... In between worked with c, c++, java, assembler, php, pascal, JavaScript, SQL based DB's and a bit of NoSQL as well.

Love software, and I'm usually fidgeting around with technology software and hardware on my free time.

Comments and Discussions

 
Questionoption 4: use DI to inject utility class your library can use Pin
Dan Mordechay26-Jul-14 21:35
Dan Mordechay26-Jul-14 21:35 
AnswerRe: option 4: use DI to inject utility class your library can use Pin
_Noctis_26-Jul-14 21:54
professional_Noctis_26-Jul-14 21:54 
GeneralRe: option 4: use DI to inject utility class your library can use Pin
Dan Mordechay26-Jul-14 23:01
Dan Mordechay26-Jul-14 23:01 
GeneralRe: option 4: use DI to inject utility class your library can use Pin
_Noctis_26-Jul-14 23:33
professional_Noctis_26-Jul-14 23:33 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.