11

I'm trying to test private methods in a Unit test project. So far it's going great, but I hit a bump, when I have to test a method with an out parameter. The signature for that method is:

private bool GotSSI(out SSI ssi, RSI rsi)
{
        ~code omitted~
}

And the unittest (the part that is not working) looks like this:

SSI ssi = null;
object[] p = new object[]{ssi,rsi};
Type[] t = new Type[] { typeof(SSI).MakeByRefType(), typeof(RSI) };
actual = (bool) privateTarget.Invoke("GotSSI",t,p);

The GotSSI method work. I've tested it in debug mode within the unit test and I can see that the 'ssi' out variable is set inside the method, before returning it's true or false value. But when the test returns to it's own code, the 'ssi' variable is still null. So the problem is that the object I created in the "GotSSI" method, is not parsed out of the PrivateObject invoke method.

Anyone knows what I am missing?

Update (Solution by Rafal)

Rafal's solution work perfectly and here is how I implemented the solution.

I created a delegate:

delegate bool GotSSIInternal(out SSI ssi, RSI rsi);

And when I have created the object I wanted to test, I build the delegate (target is the object i'm testing):

GotSSIInternal gotSSIInternal = (GotSSIInternal) Delegate.CreateDelegate(
            typeof (GotSSIInternal), 
            target,
            typeof(OfflineResolver).GetMethod("GotSSI", BindingFlags.NonPublic | BindingFlags.Instance));

After that is very simple to call the delegate:

actual = gotSSIInternal.Invoke(out ssi, rsi);

The solution is very simple and works like a charm.

evilfish
  • 661
  • 8
  • 30
  • Might make sense to define a helper class for making these for you: internal static T MakeDelegate(string methodName, T target){ return (T)Delegate.CreateDelegate(typeof(T), target, typeof(C).GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Instance));} But maybe you already thought of that. :-) – JLRishe Jan 08 '13 at 12:36
  • Not really. It was only one test that had this problem, plus I was under time pressure. A good idea non the less. – evilfish Jan 09 '13 at 14:03

5 Answers5

11

Although the final solution that was accepted works, there is a much simpler way to do it. If you follow the link given in the accepted answer from Rafal, you will find a similar question to this one, with two answers. The second answer (with the most "useful" points) is the simpler of the two.

Here is a modified version of that answer specifically for a testing scenario:

//method to test is a private method of the class ClassTotest with signature 
//    TryGetCustomerData(List<Invoice> invoices, out CustomerData customerData) 
//set up
var objToTest = new ClassToTest();
var invoices = GetInvoices();
CustomerData customerData = null;

//set up the arguments
var args = new object[] { invoices, customerData };
//get the MethodInfo of the method you want to test
var method = typeof(ClassToTest).GetMethod("TryGetCustomerData",
    BindingFlags.NonPublic | BindingFlags.Instance);
//invoke it
var success = (bool)method.Invoke(objToTest, args);
//get back the customerData argument - the "out" parameter
var actual = args[1] as CustomerData;
grahamesd
  • 4,773
  • 1
  • 27
  • 27
3

your invocation of method with out parameter is wrong if you want to get the out value. See this on how to invoke it with reflection.

Community
  • 1
  • 1
Rafal
  • 12,391
  • 32
  • 54
0

You need to ask yourself if you really need to test private methods? I personally do not test Private methods but it is all down to personal opinion (and it can get quite heated). There are numerous reasons / articles / opinions. A good SO thread can be found here.

An excerpt from the accepted answer is "A private method is an implementation detail that should be hidden to the users of the class. Testing private methods breaks encapsulation..."

The reason I do not test my private methods is because they are more likely to change than the public interface. If you cover all of your private methods it makes refactoring more complex (again, just my opinion). If you change a private method and the Public interface breaks you will know as your unit tests fail and you can then drill down.

That is just my opinion and I know many disagree so just putting it out there!

Community
  • 1
  • 1
Belogix
  • 8,129
  • 1
  • 27
  • 32
  • I'm aware of the general consensus towards unit testing private method, and normally I don't do it. Problem here is that these methods i'm testing is complex to setup from the public method I could use. Using private testing is far easier to control and understand. – evilfish Jan 08 '13 at 11:39
  • 1
    Curious who / why the down-vote? You are entitled to down-vote but it helps to comment WHY, otherwise it seems fairly pointless. – Belogix Mar 02 '15 at 10:00
0

I can use PrivateObject to do this, although it is not technically a "return value", like:

object[] p = new object[] { null, rsi };
Type[] t = new Type[] { typeof(SSI).MakeByRefType(), typeof(RSI) };
actual = (bool)privateTarget.Invoke("GotSSI", t, p);
SSI ssi = p[0];

Actually the type array can be omitted if the method overload can be determined without it:

object[] p = new object[] { null, rsi };
actual = (bool)privateTarget.Invoke("GotSSI", p);
SSI ssi = p[0];
robbie fan
  • 618
  • 1
  • 6
  • 10
0

Just do this: https://instance-factory.com/?p=738

ClassWithPrivateMethods instance = new ClassWithPrivateMethods();

PrivateObject privateObject = new PrivateObject(instance);

object[] args 
  = new object[] { "some value", null /* placeholder for out param */ };

privateObject.Invoke("PrivateMethodHavingOutParameter", args);

var outParameterValue = args[1];
palapapa
  • 573
  • 2
  • 5
  • 25