krrishna
krrishna

Reputation: 2078

How to access a Static Class Private fields to unit test its methods using Microsoft Fakes in C#

I have the below static class and a method in it which I need to unit test. I am able to But this method has the if condition which uses a Boolean private variable and if the value of it is false then it executes the steps in that if condition.

public static class Logger
{
    private static bool bNoError = true;
    public static void Log()
    {
        if (!bNoError)
        {
            //Then execute the logic here
        }
        else
        {
            //else condition logic here
        }
    }
} 

Is there a way I can set the private field bNoError value to true so that I can have one test method which tests the logic in if condition.

Upvotes: 5

Views: 5391

Answers (5)

forsvarir
forsvarir

Reputation: 10859

Sometimes, you can get so close to the code you're trying to test that you forget to think of the big picture and start focusing in too much on the implementation. Rather than thinking "When XXX happens, ...", you start thinking "When this variable is set, ...". When you get to this point, it's a sign that you might be focusing too much on the implementation and you're running a significant risk of creating very brittle tests that will break if you change anything about your implementation.

@Martin Noreke's post covered how to do what you're trying to. It feels to me though, like you may be testing the wrong thing. It seems like the next test you are going to write, is "Do XXX with Logger and test that bNoError is set to false"

Obviously it depends a bit on the rest of the Logger class, but it feels like perhaps an alternate approach might be:

Call Logger method XXX, shimming dependencies if necessary in order to trigger error state.
Call Logger.Log and validate expected behaviour

Assuming there is a way for bNoError to get reset back to true, you could then:

Call Logger method YYY, shimming dependencies if necessary to trigger error cleanup
Call Logger.Log and validate expected behaviour

Upvotes: 1

DrewJordan
DrewJordan

Reputation: 5314

Well, one way is to add a method to the original class and then shim it:

public static class Logger
{
    // .... other stuff here

    private static void SetbNoError(bool flag)
    {
        // leave implementation empty 
    }
}

Then in your test:

ShimLogger.SetbNoErrorBool = flag => bNoError = flag;

Upvotes: 0

Tomas Kubes
Tomas Kubes

Reputation: 25178

I don't recommend to use reflection on unit testing. It is difficult to maintain on large project with lot of developers, because it cannot be easily refactored, find.

Furthermore unit-test should test some behavior of class. If there is some kind of error handling in the tested class it should be accessible not only to unit-test but to the developer(invoker) in real scenario and not hidden by encapsulation. If I call some method in real scenario, I expect I can obtain error status or catch exception. Generally I prefer exception. Exception error handling can be tested easily by unit tested and no reflection to break encapsulation is needed.

So my suggestion is make it public:

public static class Logger
{
    private bool bNoError = true;
    public static void Log()
    {
        if (!bNoError)
        {
            //Then execute the logic here
        }
        else
        {
            //else condition logic here
        }
    }

    public static bool IsAnyError()
    {
        return !bNoError;
    }
} 

Upvotes: 0

Martin Noreke
Martin Noreke

Reputation: 4146

For UnitTesting purposes, Microsoft has implemented a few helper classes (PrivateType and PrivateObject) that use reflection for scenarios like this.

PrivateType myTypeAccessor = new PrivateType(typeof(TypeToAccess));
myTypeAccessor.SetStaticFieldOrProperty("bNoError", false);

PrivateType is intended for static access, whereas PrivateObject is for testing against instantiated objects instead.

You will need to include the Microsoft.VisualStudio.TestTools.UnitTesting namespace from the Microsoft.VisualStudio.QualityTools.UnitTestFramework.dll in order to use these.

Upvotes: 11

vendettamit
vendettamit

Reputation: 14687

You can use Reflection to do that for testing purpose. Though it's quite outlaw.

using System.Reflection;
................
................
var field = typeof(Logger).GetField("bNoError", 
                            BindingFlags.Static | 
                            BindingFlags.NonPublic);

        // Normally the first argument to "SetValue" is the instance
        // of the type but since we are mutating a static field we pass "null"
        field.SetValue(null, false);

Upvotes: 6

Related Questions