![]() |
Languages »
C# »
General
Intermediate
How to Test Private and Protected methods in .NETBy TimStallThis article explains some theory behind testing/not testing private methods, and then provides and walks through a downloadable code sample to demonstrate these testing techniques. |
C#.NET 1.1, Win2003VS.NET2003, Dev
|
|
Advanced Search Add to IE Search |
|
|
|
||||||||||||||||
Test Driven Development is the practice of (1) writing tests, (2) writing code that passes those tests, and (3) then refactoring. This concept is becoming very popular in the .NET community due to the quality assurance that it adds. While it is easy to test public methods, the natural question emerges "How do I test protected and private methods?"
This article will:
A Google search will show you that there's a lot of debate about using private methods, let alone testing them. The table below summarizes some of the common views of the pro and con for both issues.
| Pro | Con | |
| Use private methods |
|
|
| Test private methods |
|
|
There are bright and experienced people on both sides of the issue. So while I have no intention or expectation of ending the "should I test private method" debate, there is still value for both sides to know how to test them. Even if you think that private methods should not be tested:
Andrew Hunt and David Thomas explain in their book, Pragmatic Unit Testing in C# with NUnit, that good unit tests are ATRIP:
There are three additional criteria that any testing overhead for private/protected methods should meet:
Keeping these criteria in mind, there are several strategies that fall short:
| Strategy | Problem |
| Don't have any private methods. |
|
Use the directives #if DEBUG ... #endif
to wrap a public method which in turns wraps the private method. The unit tests
can now indirectly access that private method through the public wrapper. (This
is a method that I myself have used many times, and found it to be tedious and
non-object oriented). |
|
Use the [Conditional("DEBUG")] attribute
on public methods that wrap the private methods. |
|
| Create internal methods to access the private method; then have a public test class elsewhere in the assembly that wraps those internal methods with public ones. |
|
A protected method is visible only to derived classes, therefore it is not
immediately available to a test suite. For example, suppose we wanted to test
the method from ClassLibrary1.MyObject:
protected string MyProtectedMethod(string strInput, int i32Value)
{
return this.Name + ": " + strInput + ", " +
i32Value.ToString();
}
The book Pragmatic
Unit Testing in C# with NUnit explains one solution: make a derived class
MyObjectTester that inherits class MyObject, and then
create a public method TestMyProtectedMethod that wraps the
protected one. For example:
public new string TestMyProtectedMethod(string strInput, int i32Value)
{
return base.MyProtectedMethod(strInput,
i32Value);
}
This approach is simple, yet meets all the criteria:
| Criteria | Fulfillment |
| Transparency | By using inheritance and putting the
MyObjectTester class in the UnitTests assembly, it
doesn't add any new code to the production assembly. |
| Scope | Nothing in this approach depends on Debug-only techniques. |
| Simplicity | Although this approach requires a new derived class, and an additional public wrapper method for each protected method, it is object-oriented and type safe. |
Testing private methods is a little more involved; but we can still do it using System.Reflection. You can use Reflection to dynamically access methods of a type, including both instance and static private methods. Note that accessing private methods does require the ReflectionPermission, but that is not a problem for Unit Tests running on a development machine or build server.
Suppose we wanted to test the private method MyPrivateMethod
from ClassLibrary1.MyObject:
private string MyPrivateMethod(string strInput, DateTime dt, double
dbl)
{
return this.Name + ": " + strInput + ", " +
dt.ToString() + ", " + dbl.ToString();
}
One solution is to create a UnitTestUtilities project with a helper class to
call the test method via reflection. For example, the download solution has the
following methods in UnitTestUtilities.Helper:
public static object RunStaticMethod(System.Type t, string strMethod,
object [] aobjParams)
{
BindingFlags eFlags =
BindingFlags.Static | BindingFlags.Public |
BindingFlags.NonPublic;
return RunMethod(t, strMethod,
null, aobjParams, eFlags);
} //end of method
public static object RunInstanceMethod(System.Type t, string strMethod,
object objInstance, object [] aobjParams)
{
BindingFlags eFlags = BindingFlags.Instance | BindingFlags.Public |
BindingFlags.NonPublic;
return RunMethod(t, strMethod,
objInstance, aobjParams, eFlags);
} //end of method
private static object RunMethod(System.Type t, string
strMethod, object objInstance, object [] aobjParams, BindingFlags eFlags)
{
MethodInfo m;
try
{
m = t.GetMethod(strMethod, eFlags);
if (m == null)
{
throw new ArgumentException("There is no method '" +
strMethod + "' for type '" + t.ToString() + "'.");
}
object objRet = m.Invoke(objInstance, aobjParams);
return objRet;
}
catch
{
throw;
}
} //end of method
Private method RunMethod takes in the necessary parameters that
Reflection needs to invoke a method, and then returns the value. It has two
public methods that wrap this: RunStaticMethod and
RunInstanceMethod for static and instance methods respectively.
Walking through RunMethod, it first gets the MethodInfo
from a type. Because we expect this to only be called for existing
methods, a null method triggers an Exception. Once we have the
MethodInfo, we can invoke the method given the instantiated object
(null for static methods) and the parameter array.
We could use this Utility in an NUnit test like so:
[Test] public void TestPrivateInstanceMethod()
{
string strExpected = "MyName: Hello, 5/24/2004
12:00:00 AM, 2.1";
ClassLibrary1.MyObject objInstance
= new MyObject("MyName");
object obj =
UnitTestUtilities.Helper.RunInstanceMethod(
typeof(ClassLibrary1.MyObject), "MyPrivateMethod",
objInstance, new object[3] {"Hello",
new DateTime(2004,05,24), 2.1});
string strActual = Convert.ToString(obj);
Assert.AreEqual(strExpected,strActual);
}
| Criteria | Fulfillment |
| Transparency | The only extra code we created -
UnitTestUtilities, is not shipped in production. |
| Scope | Nothing in this approach depends on Debug-only techniques. |
| Simplicity | This approach can call any method with a single call.
Once you have the UnitTestUtilities, the only complication is
creating the correct parameters (method name, data types, etc...) for
RunInstanceMethod or RunStaticMethod. Because the
method is being dynamically called, the parameters aren't checked at compile
time. |
While there is a debate on whether or not to test private methods, at least
we have the ability to do so. We can test protected methods using inheritance to
create a derived TesterClass that wraps the base protected methods
with public ones. We can test private methods using Reflection, which can be
abstracted to a UnitTestUtility helper class. Both of these
techniques can help to improve test coverage.
General
News
Question
Answer
Joke
Rant
Admin
|
PermaLink |
Privacy |
Terms of Use
Last Updated: 1 Mar 2005 Editor: Nishant Sivakumar |
Copyright 2005 by TimStall Everything else Copyright © CodeProject, 1999-2009 Web21 | Advertise on the Code Project |