Non-code-generated forwarding shim for testing private methods
Asked Answered
P

3

2

In general, I design classes in such a manner as to not require access to privates for testing purposes. An InternalsVisibleTo can also assist.

However, I'm currently dealing with a codebase that has a few area which have become reliant on the [private accessors mechanism in VSTS](http://msdn.microsoft.com/en-us/library/ms184807(VS.80).aspx) (i.e., using VSCodeGenAccessors to generate *_Accessor classes which have forwarding that use reflection to invoke private members (and optionally internal ones too) on a class.

So I've got code like:

ClassUnderTest target = new ClassUnderTest();
var accessor = ClassUnderTest_Accessor.AttachShadow( target );
accessor.PrivateMethod();
Assert.True( accessor._privateMethodWasCalled);
accessor.PrivateProperty = 5;
Assert.Equal( accessor.PrivateProperty, 5);

(Yes, riddled with antipatterns - but please dont shoot the messenger)

I have a number of problems with this:

  1. I'd like to be able to clarify which I privates I need
  2. I'd prefer not to be invoking dialogs (Yes, I'm a CRaholic)
  3. I'd prefer not to involve code generation in the picture

So I'd like to be able to transform the above code to something like:

var target = new ClassUnderTest();
IClassUnderTestInterface accessor = Shadow.Create<IClassUnderTestInterface>( target );
accessor.PrivateMethod();
Assert.True( accessor._privateMethodWasCalled);
accessor.PrivateProperty = 5;
Assert.Equal( accessor.PrivateProperty, 5);

With only the following interface sitting in my test assembly, and no generated code or custom build steps :-

interface IClassUnderTestInterface
{
   int PrivateProperty {get; set;}
   bool _privateMethodWasCalled {get; }
   void PrivateMethod();
}

From there, I'd be able to use CodeRush or Ctrl K M to generate new shadow methods onto the interface with only a keystroke.

The missing bit would be having a method I Shadow.Create<I>( Object o) which would 1. generate a dynamic proxy that implements the interface 1. verify that the object o to be wrapped has all the members dictated by the interface 1. bnous: manage the forwarding of properties representing fields (i.e., the `_privateMethodWasCalled' case) correctly

So, does anyone know a library that implements something like this (or feel bored enough to write it?)

One obvious drawback is that youo dont know if the interface isnt commpatible with the ClassUnderTest until runtime, but that's OK as this would only be for tests. Also AIUI, the private accessors mechanism also requires the triggering of a recompile to sync stuff up from time to time.

Or is there a way better approach I'm missing? (Remembering I dont want to go blanket ugrading al privates to internal or public and dont want to have to rewrite working code)

Using xUnit.net, .NET 3.5; Open to using any dynamic proxy library or other

Publicness answered 25/6, 2009 at 13:23 Comment(0)
P
1

I ended up routing around all this stuff (InternalsVisibleTo, separate test projects, Test References, private accessors, the MSBuild Shadow task / publicize.exe failing randomly in builds, using reflection to access privates) by merging the tests into the main projects and making anything that needed to be accessed from tests internal). See also xUnit.net Test Stripper [to remove test code embedded in binaries prior to deployment/shipping]

Publicness answered 26/11, 2009 at 16:38 Comment(2)
github.com/markrendle/Trespasser/tree/master/Trespasser Looks like it's just the job, but I'm not in a position to trial it right nowPublicness
Via @ajasinski there was an earlier one igoro.com/archive/… (But Trespasser looks fine and is on NuGet)Publicness
I
1

Have you looked at mocking frameworks like Moq or Rhino? In your case they could help IF you are willing to change the privates that your need to test to 'protected virtual' (which is not as bad a going public of internal). Basically, if the member is virtual, then mocking framework can generate a subclass that records what members are called.

Inroad answered 26/6, 2009 at 12:34 Comment(2)
Interesting idea. Whether making something protected to facilitate testing is better than making it internal is slightly debatable though IMO... ? But virtual is possible and might help in getting it implemented [still via LazyWeb of course]. Thanks for the idea. (Out of +1s for the day but one will follow in due course!)Publicness
Of course the key thing is that I'm pretty much looking for the implemenation here....Publicness
P
1

I ended up routing around all this stuff (InternalsVisibleTo, separate test projects, Test References, private accessors, the MSBuild Shadow task / publicize.exe failing randomly in builds, using reflection to access privates) by merging the tests into the main projects and making anything that needed to be accessed from tests internal). See also xUnit.net Test Stripper [to remove test code embedded in binaries prior to deployment/shipping]

Publicness answered 26/11, 2009 at 16:38 Comment(2)
github.com/markrendle/Trespasser/tree/master/Trespasser Looks like it's just the job, but I'm not in a position to trial it right nowPublicness
Via @ajasinski there was an earlier one igoro.com/archive/… (But Trespasser looks fine and is on NuGet)Publicness
P
0

This neat trick that can make forwarding functions more succint, although its still manual in nature

Also, this answer covers how to handle static members (but requires CLR4 which wasnt in play at the time of my asking and doesn't handle private methods without bringing InternalsVisibleTo into the mix).

Publicness answered 12/2, 2010 at 9:27 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.