Caves Travel Diving Graphics Mizar Texts Cuisine Lemkov Contact Map RSS Polski
Trybiks' Dive Texts Testing Accessing private and protected members - PrivateObject and PrivateType YAC Software
  Back

List

Charsets

Charts

DBExpress

Delphi

HTML

Intraweb

MSTest

PHP

Programming

R

Rhino Mocks

Software

Testing

UI Testing

VB.NET

VCL

WPF

Accessing private and protected members - PrivateObject and PrivateType
Usually, I try to implement my unit tests by testing only publicly available members of a class. These kinds of tests are called black-box tests since they don't care what's happening inside the entity being tested, but only what outputs it generates for any given input.

I find, however, that often you'll also want to test protected and private members of a class. Some of the possible reasons for doing that:
  • a private method performs calculations that are not that easy to test via accessing public methods only (all code paths are hard to execute, the algorithm is pretty complex, the test is difficult to setup without accessing that method, etc.),
  • a private field needs to be set to a specific value for the test to run, but you don't want to make that field neither protected nor public,
  • methods are hidden not because their implementation may change, but because you want to show developers how they should access an object (so, the argument of doing black-box testing because the internals may change doesn't really apply in this scenario),
  • and probably a hundred other reasons for very specific scenarios.
Though you may argue that some of the above are caused by poor design decisions, in real life it's just not that simple (for instance, you may have inherited a substantial code base and it's not feasible to restructure the code)...

Anyway, if you find that you need to access private fields and/or methods, PrivateObject comes to the rescue.

Let's say that we have the following class:
  Public Class Comp
  
    Private _field As Integer
  
    Private Function GetMultiple(ByVal multiplier As Integer) As Integer
      Return _field * multiplier
    End Function
  
    . . .
  
  End Class
Now, because of... whatever..., you need to access these private members in a unit test:
  <TestMethod()>
  Public Sub TestComp
    Dim comp As New Comp()
    Dim compAccessor As New PrivateObject(comp)
    . . .
  End Sub
compAccessor is instantiated with the class instance under test; now, using compAccessor, you can access comp's private members:
  <TestMethod()>
  Public Sub TestGetMultiple
    Dim comp As New Comp()
    Dim compAccessor As New PrivateObject(comp)
  
    compAccessor.SetField("_field", 2)
  
    Dim result As Integer = CInt(compAccessor.Invoke("GetMultiple", 5))
  
    Assert.AreEqual(10, result,
                    "GetMultiple shall return the product of the given value " +
                    "and the value of the internal _field variable.")
  End Sub
So, you can use SetField to set a private variable's field to a value (and GetField to retrieve that value). And than, you can use Invoke to call a sub or a function; and to retrieve the results of a function.

Note that you need to cast the result of Invoke to the target type. This is because Invoke is declared as returning an Object (since it can be called with various functions that return values of types other than Integer).

If you need to pass in multiple values to a method, you just pass an array of values (let's say that now GetMultiple takes two integers):
  <TestMethod()>
  Public Sub TestGetMultiple
    Dim comp As New Comp()
    Dim compAccessor As New PrivateObject(comp)
  
    Dim result As Integer = CInt(compAccessor.Invoke("GetMultiple", {5, 2}))
  
    Assert.AreEqual(10, result,
                    "GetMultiple shall return the product of the given values.")
  End Sub
If you have a private or protected property, instead of using GetField/SetField, you should use GetProperty/SetProperty. However, it's usually best to just use GetFieldOrProperty/SetFieldOrProperty to handle both scenarios

And, just like with Invoke, you'll need to cast the results of GetField/GetProperty/GetFieldOrProperty to the target type.

Finally, Invoke can also be used with Subs (with zero or more parameters) - it just doesn't return a value then.



Now, this gives you access to instance members; but if you want to access private shared / static members, instead of using PrivateObject, you'll need PrivateType.

Works just like private object, but instead of initializing it with an instance, you pass the class type to the constructor; and, instead of using Invoke and Get/SetX, you need to use InvokeStatic and Get/SetStaticX (e.g. GetStaticField, GetStaticProperty, GetStaticFieldOrProperty, and SetStaticField, SetStaticProperty, SetStaticFieldOrProperty):
  <TestMethod()>
  Public Sub TestGetMultiple
    Dim compAccessor As New PrivateType(GetType(Comp))
  
    compAccessor.SetStaticField("_field", 2)
  
    Dim result As Integer = CInt(compAccessor.InvokeStatic("GetMultiple", 5))
  
    Assert.AreEqual(10, result,
                    "GetMultiple shall return the product of the given value " +
                    "and the value of the internal _field shared variable.")
  End Sub
Happy testing!

Top

Comments
#1
Eddie wrote on 2012-12-13 21:14:08
What about a private/protected method that has out parameters?
#2
Trybik wrote on 2012-12-18 21:07:01
Answered here.

Top

Add a comment (fields with an asterisk are required)
Name / nick *
Mail (will remain hidden) *
Your website
Comment (no tags) *
Enter the text displayed below *
 

Top

Tags

Testing

VB.NET


Related pages

AssertWasCalled and Methods Called Multiple Times

AssertWas[Not]Called and Object Properties

Rhino Mocks's AssertWasNotCalled

Visual Studio - moving coded UI tests to a new / different project results in null reference exceptions

PrivateObject and Out/ByRef parameters

PrivateObject, WithEvents, and generics

Accessing private members of base classes

CA1800:DoNoCastUnnecessarily

PrivateObject and WithEvents

The creator of this fault did not specify a Reason.

VS - Test Run Error - "COM object that has been separated from its underlying RCW cannot be used"

Saving / restoring window placements in .NET

Get the TreeViewItem for an Item in a WPF TreeView

Output in MSTest Tests (VS 2010)

Automated WPF tests and "Invalid URI: Invalid port specified."

Checking Property Change Notifications

Checking "Dangling" Event Handlers in Delphi Forms

Rhino Mocks's AssertWasCalled in VB.NET

First steps with Rhino Mocks (in VB.NET)

Meaningful identifiers

Public fields vs. properties

VS Pending Tests

Automated GUI Testing

Automated GUI Testing in VMs

Automated Testing of Window Forms

Detecting Memory Leaks with DUnit