Testing Non-Public classes and Non-public constuctors.

12 views
Skip to first unread message

Vadim

unread,
Apr 12, 2007, 1:46:37 PM4/12/07
to MbUnit.Dev, asto...@gmail.com
I extended Reflector class to test non-public classes and non-public
constructors. My question is what the best place to put the modified
files?

Here's an example of testing Microsoft internal struct __DTString that
is part of mscorlib.dll.

[Test]
public void InternalConstructorTest()
{
DateTimeFormatInfo dtfi = new DateTimeFormatInfo();
Reflector _reflector = new Reflector("mscorlib.dll",
"System.__DTString", "MbUnit", dtfi);
Assert.AreEqual(true, _reflector.RunPrivateMethod("Match",
"MbUnit"));
}

Vadim

Jeff Brown

unread,
Apr 18, 2007, 10:42:43 PM4/18/07
to MbUn...@googlegroups.com
Just a thought, I think the naming conventions of Reflector are somewhat
inconsistent.
It contains GetNonPublicVariable and GetPrivateVariable among other
things. These methods are actually are capable of accessing public
fields too. Also the .Net framework refers to fields not variables and
to invoking a method not running it.

Much of the design of Reflector seems to be focused on accessing
non-public members but I can see that it is also of use accessing the
contents of late-bound types such as COM objects that do not have
registered wrappers.

How about we update the names of these methods like so:

GetField : Gets a public field
SetField : Sets a public field
GetProperty : Gets a public property
SetProperty : Sets a public property
InvokeMethod : Invokes a public method
AddEventHandler
RemoveEventHandler

GetNonPublicField : Gets a non-public field
SetNonPublicField : Sets a non-public field
GetNonPublicProperty : Gets a non-public property
SetNonPublicProperty : Sets a non-public property
InvokeNonPublicMethod : Invokes a non-public method
AddNonPublicEventHandler
RemoveNonPublicEventHandler

(Also provide static flavours like GetStaticField, SetStaticField,
etc...)

I also dislike having to specify the assembly partial name like that.
How about a bunch of factory methods?

Reflector Reflector.CreateInstance(Type type, params object[] args);
Reflector Reflector.CreateInstance(string typeName, params object[]
args);
Reflector Reflector.CreateInstanceFromAssembly(string assemblyName,
string typeName, params object[] args);

A few other utilities could come in handy (though these are just
one-liners):

Assembly Reflector.GetAssembly(string assemblyName);
Type Reflector.GetType(string type);
Type Reflector.GetTypeFromAssembly(string assemblyName, string
typeName);
Delegate Reflector.CreateDelegate(Type delegateType, object
targetObject, MethodInfo targetMethod);

It would also be useful to provide the following getters:

Type Type { get; } : Gets the type of the reflected object.
object Instance { get; } : Gets the contained instance.

Finally, method invocation should automatically unwrap Reflector
instances passed in as parameters before calling the method.

Jeff.

Vadim

unread,
Apr 24, 2007, 9:10:41 AM4/24/07
to MbUnit.Dev
Jeff,

Sorry, it took me awhile to reply to your post. I was very busy at
work dealing with crisis. I barely had time to read (not to reply) my
emails.

> Just a thought, I think the naming conventions of Reflector are somewhat
> inconsistent.

I agree some naming conventions are inconsistent. Unfortunately, it's
not very easy to change the names because we don't know how many
people are using Reflector. What we can do is to decorate old methods
with Obsolete attribute and remove them in version 3.0. However, I
guess we need other people opinion.

Andrew, Do you have any ideas?

> It contains GetNonPublicVariable and GetPrivateVariable among other
> things.

GetNonPublicVariable is a static method where GetPrivateVariable is an
instance method.
I personally prefer to work with instance methods.

GetNonPublicVariable is decorated with Obsolete attribute (see snippet
of code below) and prefer method is GetNonPublicField.

[Obsolete("Use GetNonPublicField instead")]
public object GetPrivateVariable(string variableName)

> Much of the design of Reflector seems to be focused on accessing
> non-public members but I can see that it is also of use accessing the
> contents of late-bound types such as COM objects that do not have
> registered wrappers.

Ability to access public members is a free bonus that doesn't cost
much to implement; however, I don't see why people will use Reflector
to access public members when they can access the directly. Actually,
people might use Reflector, to access public members of non-public
class or struct. GetNonPublicXXX name was chosen on purpose. I
believe that this is a very good discussion. I'd like to here other
people opinion.

I have to run now but I'll try to talk more on this subject.

Jeff, thanks again for talking about this.

Vadim

> Cc: astopf...@gmail.com

Andrew Stopford

unread,
Apr 25, 2007, 11:08:16 AM4/25/07
to MbUn...@googlegroups.com
Hi Vadim,

If we settle for a scheme in Gallio then if anything is obsolete we can indicate it in the migration documentation, making sure we have clean and agreed syntax for Gallio will be important.

Andy

> Reflector Reflector.CreateInstanceFromAssembly (string assemblyName,

Vadim

unread,
Apr 26, 2007, 7:54:00 AM4/26/07
to MbUnit.Dev
We can rename Reflector members and create new ones as Jeff suggests.
I'm not sure if we need to do different implementation for public, non-
public, and static members. I believe that in most cases when people
need to access public members on a public class/struct, they will do
it directly without using the Reflector class.


What do you think about these names (Jeff suggested them for public
members only).

GetField : Gets a public/non-public/static field (Exists: needs to be
renamed)
SetField : Sets a public/non-public/static field (New: needs to be
created)
GetProperty : Gets a public/non-public/static property (Exists)
SetProperty : Sets a public/non-public/static property (New)
InvokeMethod : Invokes a public/non-public/static method (Exists)
AddEventHandler (New)
RemoveEventHandler (New)

I like suggestion about CreatInstance. The method below is fine.
CreateInstanceFromAssembly(string assemblyName, string typeName,
params object[] args);

we also can add these methods:
CreateInstanceFromAssembly(string assemblyName, string typeName);
CreateInstanceFromAssembly(Assembly assembly, string typeName);
CreateInstanceFromAssembly(Assembly assembly, string typeName, params
object[] args);

However, the methods below need to know from which assembly they need
to create instance.
CreateInstance(Type type, params object[] args);
CreateInstance(string typeName, params object[] args);

We can create an Assembly property. That means that a user needs to
set assembly before calling the two methods above. If assembly is not
set, we have to throw an exception. Is that what we want?


Here a list of utilities Jeff suggested:

Assembly Reflector.GetAssembly(string assemblyName);
Type Reflector.GetType(string type);
Type Reflector.GetTypeFromAssembly(string assemblyName, string
typeName);
Delegate Reflector.CreateDelegate(Type delegateType, object
targetObject, MethodInfo targetMethod);

Type Type { get; } : Gets the type of the reflected object.


object Instance { get; } : Gets the contained instance.

Do you think it's going to be need for them?

Vadim

Phil Haack

unread,
Apr 27, 2007, 4:03:46 AM4/27/07
to MbUn...@googlegroups.com

Hi All,

 

I’m writing some new Custom Test Attributes (inheriting from DecoratorPatternAttribute) and I was wondering if there’s any way to get the test method from within my RunInvoker?

 

The reason I need this is I want my invoker to extract a resource from the assembly being tested. But at the time that the invoker is being invoked, the test using my custom attribute isn’t in the call stack. I figure this would be information that MbUnit would have at the moment, but it doesn’t appear to be passing that info to the invoker.

 

Is there something I’m missing?

 

Phil

Jay Flowers

unread,
Apr 27, 2007, 7:45:12 AM4/27/07
to MbUn...@googlegroups.com
Phil,
I would guess that your new attribute includes info about the resource.  If so I over came this issue by handling it from my own IRun class in the Reflect method.  Notice the call to attribute1.GetData.ParameterCollection .  This is getting the resource info.  It is stored in the var TestParameter and passed to the test runinvoker's constructor.  You could pass anything to the runinvoker in this fashon.

The var names are not all that nice because I figured this out by snagging it from Reflector.  I forget why I needed it in VB.NET.

Public Sub Reflect(ByVal tree As RunInvokerTree, ByVal parent As RunInvokerVertex, ByVal t As Type) Implements IRun.Reflect

    Dim TestMethods As ICollection = TypeHelper.GetAttributedMethods(t, GetType(TestWithEachObjectAttribute))

    Dim FixtureObjectProviders As Object() = t.GetCustomAttributes( GetType(ObjectProviderFixtureDecoratorAttribute), True)

    Dim num1 As Integer

    For num1 = 0 To FixtureObjectProviders.Length - 1

        Dim attribute1 As ObjectProviderFixtureDecoratorAttribute = CType(FixtureObjectProviders(num1), ObjectProviderFixtureDecoratorAttribute)

        Dim TestParameter As Object

        For Each TestParameter In attribute1.GetData.ParameterCollection

            Dim TestMethodInfo As MethodInfo

            For Each TestMethodInfo In TestMethods

                Dim attribute2 As TestWithEachObjectAttribute = CType(TypeHelper.GetFirstCustomAttribute (TestMethodInfo, GetType(TestWithEachObjectAttribute)), TestWithEachObjectAttribute)

 

                Dim invoker1 As IRunInvoker = New TestWithEachObjectRunInvoker( Me, TestMethodInfo, attribute2, TestParameter)

                invoker1 = DecoratorPatternAttribute.DecoreInvoker(TestMethodInfo, invoker1)

                tree.AddChild(parent, invoker1)

            Next

        Next

    Next num1

End Sub

--
Jay Flowers
----------------------------------------------------------------------
http://jayflowers.com
---------------------------------------------------------------------

Phil Haack

unread,
Apr 27, 2007, 12:25:56 PM4/27/07
to MbUn...@googlegroups.com

Thanks Jay? So is that what VB looks like these days? ;)

 

I noticed the “Reflect” method but wasn’t sure what it was used for. Is there any documentation for it? I’m assuming it is called before Execute and gives me the ability to pass information to the Invoker about the code being tested?

 

What I’m trying to do is something like this:

 

[Test]

[ExtractResource(“Resource.Name”, “d:\Destination”)]

Public void SomeTest()

{}

 

Which would extract the specified resource into the destination folder. Could be a useful test attribute to contribute to MbUnit, unless someone already did it. The resource would be embedded in the same assembly as the unit test. I guess I might also need an override that allows you to specify the assembly to extract from.

 

Phil

Phil Haack

unread,
Apr 27, 2007, 2:33:01 PM4/27/07
to MbUn...@googlegroups.com

Hi All,

 

With some friendly help from Jay, I got my new test decorator working.

 

http://haacked.com/archive/2007/04/27/extract-embedded-resources-with-an-attribute-in-mbunit.aspx

 

My hope is this finds its way into a future release of MbUnit. *hint* *hint*.

 

Or at least some sort of MbUnitContrib project. :D

 

Phil

Jeff Brown

unread,
Apr 27, 2007, 6:14:48 PM4/27/07
to MbUn...@googlegroups.com
The trick is how to support access to late-bound types easily.
Late-binding is usually used because the type or some of its members are
inaccessible but that's not the only interesting case. Sometimes the
type is loaded dynamically, generated at runtime, or simply does not
implement an accessible interface.

Indicating public vs. non-public is useful for documentation purposes.
Indicating static usage in some fashion is essential since the calling
conventions are different. Does an instance of Reflector exist when
accessing static members, or do we just call static functions on
Reflector with a suitable Type? Or... how about making a TypeReflector
to wrap a Type with extra convenience methods for accessing statics and
then make Reflector just wrap intances.


In any case, an assembly is not strictly required for implementing
CreateInstance with just a Type or typeName. These can be implemented a
bit like this:

static Reflector CreateInstance(Type type, params object[]
constructorArgs)
{
return Wrap(Activator.CreateInstance(type,
BindingFlags.CreateInstance | BindingFlags.Public |
BindingFlags.NonPublic |BindingFlags.Instance, null,
UnwrapArray(constructorArgs)));
}

// assumes typename is assembly qualified or in the currently executing
assembly
static Reflector CreateInstance(string typeName, params object[]
constructorArgs)
{
return CreateInstance(Type.GetType(typeName, true),
constructorArgs));
}

static object[] UnwrapArray(object[] values)
{
if (values == null)
return null;

return Array.ConvertAll(values, delegate(object value)
{
Reflector reflectedValue = value as Reflector;
return reflectedValue != null ? reflectedValue.Instance : value;
});
}

static Reflector Wrap(object value)
{
Reflector reflectedValue = value as Reflector;
return reflectedValue != null ? reflectedValue : new
Reflector(reflectedValue);
}


Additional helpers would be useful for situations involving private
nested types, creating instances of privately typed delegates, and
various other special cases.

Jeff.

-----Original Message-----
From: MbUn...@googlegroups.com [mailto:MbUn...@googlegroups.com] On
Behalf Of Vadim

Vadim

unread,
Apr 29, 2007, 6:15:15 PM4/29/07
to MbUnit.Dev
I just want to let you know that some Jeff's suggestions were
implemented in checked in. I also blogged about some of them @
http://vadim-net.blogspot.com/2007/04/testing-non-public-members-with-mbunit_29.html.

Andrew,
Any idea when you're going to release next build? At my work people
want to use MbUnit to test non-public classes. It was checked in
about two weeks ago.

Vadim

> Vadim- Hide quoted text -
>
> - Show quoted text -

Andrew Stopford

unread,
Apr 29, 2007, 7:31:27 PM4/29/07
to MbUn...@googlegroups.com
Hi,
 
I'll try and get an interim release up on mbunit.com in the next few days.
 
Andy
 
Reply all
Reply to author
Forward
0 new messages