Unit Testing: Exposing Private Members
Posted
Thu, Oct 29 2009 16:19
by
Deborah Kurata
Visual Studio 2008 (Professional Edition and above) provides a really nice set of tools for development and execution of unit tests. It is, of course, easy for your tests to access the public properties and methods of your classes. But what about those private members?
[To begin with an overview of unit testing, start here.]
[To expose internal (C#) or friend (VB) members, see this post.]
You may have properties or methods in your class defined to be private. This post details how to test those methods using the unit testing features in Visual Studio.
NOTE: There are some testing philosophies that recommend you never test your private members; rather you test the public/internal/friend members and assume that those will provide coverage for all of your private members as well. Regardless of your point of view on this topic, this post provides the how-to so you can decide for yourself whether you want to or not.
Let's say your Customer class has a CustomerId property that looks like this:
In C#:
private int? _CustomerId;
public int? CustomerId
{
get { return _CustomerId; }
private set
{
if (_CustomerId == null || !_CustomerId.Equals(value))
{
string propertyName = "CustomerId";
if (!_CustomerId.Equals(value))
{
_CustomerId = value;
}
}
}
}
In VB:
Private _CustomerId As Integer?
Public Property CustomerId() As Integer?
Get
Return _CustomerId
End Get
Private Set(ByVal value As Integer?)
If _CustomerId Is Nothing OrElse _
Not _CustomerId.Equals(value) Then
Dim propertyName As String = "CustomerId"
If Not _CustomerId.Equals(value) Then
_CustomerId = value
End If
End If
End Set
End Property
Notice in both cases, the setter is private. The expectation is that the business object will set this value. However, you can you unit test it if you cannot set it?
The answer is PrivateObject.
PrivateObject is a class that allows test code to call class members that are not public. You PrivateObject to access private members of your class in your unit tests.
NOTE: You can also use PrivateObject for other non-public class members.
The following demonstrates how to use PrivateObject.
In C#:
/// <summary>
///A test for CustomerId
///</summary>
[TestMethod()]
public void CustomerIdTest()
{
Customer target;
int? expected;
int? actual;
// Null to Value
target = new Customer();
expected = 42;
PrivateObject po = new PrivateObject(target);
po.SetProperty("CustomerId", expected);
actual = target.CustomerId;
Assert.AreEqual(expected, actual, "Values are not equal");
}
In VB:
'''<summary>
'''A test for CustomerId
'''</summary>
<TestMethod()> _
Public Sub CustomerIdTest()
Dim target As Customer
Dim expected As Integer?
Dim actual As Integer?
' Null to value
target = New Customer
expected = 42
Dim po As PrivateObject = New PrivateObject(target)
po.SetProperty("CustomerId", expected)
actual = target.CustomerId
Assert.AreEqual(expected, actual, "Values are not equal")
End Sub
NOTE: This is only part of the code you would need to fully test the CustomerId property. Only one testing scenario was covered to instead focus on the PrivateObject syntax.
The code creates a new instance of PrivateObject, passing the instance of the class that has the private member you wish to access.
PrivateObject has methods such as GetProperty, SetProperty, GetField, SetField, and Invoke. These methods provide access to the private or other non-public members of your class.
Use PrivateObject whenever you need to access a private member in your class from your unit testing code.
Enjoy!