unit testing with objects from 3rd party assemblies

Go To StackoverFlow.com

2

I have a Visual Studio 2008 C# .NET 3.5 project that I am implementing unit tests for, but I've run in to a problem. My code references a 3rd party assembly that implements objects with internal constructors.

For example:

// in 3rd party assembly
public class Bar
{
    // internal constructor
    internal Bar();
    public int Id { get; }
    public string Name { get; }
    public Foo Foo { get; }
}

public class Foo
{
    // internal constructor
    internal Foo();
    public Collection<Bar> GetBars();
}

One method of mine that I would like to unit test is this:

// in my assembly
public static Bar FindByName(this Collection<Foo> c, string name)
{
    // search through the Foos to find the first bar with the matching name
}

And test it like this:

void TestMethod()
{
    Collection<Foo> foo_pool = new Collection<Foo>()
    {
        new Foo() { /*..*/ } // Error! ctor is inaccessible
    };

    Bar b = foo_pool.FindByName("some_name");
    assert_equal (b.Name, "some_name");
}

But, I can't create objects of type Foo or type Bar. So, how can I unit test my method?

Thanks

2012-04-05 16:22
by PaulH
Are you sure there are internal constructors? The way you've written Foo and Bar displays implicit constructors. Unless you specifically have internal Foo() { }, the compiler will generate a parameterless constructor with the same visibility as the class - Jim Schubert 2012-04-05 16:27
Yes, I am sure. The object browser shows them as internal. I have updated the code to make this explicit - PaulH 2012-04-05 16:32


3

For unit tests, you can use the PrivateObject class (namespace Microsoft.VisualStudio.TestTools.UnitTesting) to create objects with private constructors and even test private methods.

http://msdn.microsoft.com/en-us/library/microsoft.visualstudio.testtools.unittesting.privateobject(v=vs.90).aspx

http://www.gangleri.net/2007/11/15/PrivateObjects.aspx

Here's an example:

[TestMethod]
public void TestMethod2()
{
    // Arrange
    var po = new PrivateObject(typeof(MyObject));
    var obj = (MyObject)po.Target;
    // Act
    var result = obj.Calculate(2);
    // Assert
    Assert.AreEqual(3, resul);
}

public class MyObject
{
    internal MyObject()
    {

    }

    public int Calculate(int a)
    {
        return 1 + a;
    }
}

It uses reflection in the same way as Jim's suggestion, but PrivateObject class encapsulates all the work to create the instance with private constructors.

2012-04-05 16:46
by Fabio
That's very neat, but I get a Cannot convert type PrivateObject to Foo error when I try to cast between the two - PaulH 2012-04-05 16:55
It's strange because I've already used this approach in some of my projects. Can you provide the code of your unit test where you tried to use this - Fabio 2012-04-05 17:02
Are you trying to cast the PrivateObject instance to Foo or "Target" property of the PrivateObject instance to Foo - Fabio 2012-04-05 17:11
You got me. I missed the .Target part. Thanks for pointing out the issue - PaulH 2012-04-05 17:24
+1 because I haven't heard of this. However, it will tie users to MSTest, which can't be deployed separately from Visual Studio to CI servers - Jim Schubert 2012-04-06 00:52


1

You can use reflection to create objects with non-public constructors. See this question on SO.

Here's Ani's solution from the above link:

BindingFlags flags = BindingFlags.NonPublic | BindingFlags.Instance;
 CultureInfo culture = null; // use InvariantCulture or other if you prefer
 object instantiatedType =   
   Activator.CreateInstance(typeToInstantiate, flags, null, parameter, culture);

Activator.CreateInstance will find the correct constructor based on the parameters you give it.

2012-04-05 16:32
by Jim Schubert


0

If the classes arent sealed/notinheritable, then you can derive a "mocked" class from the test target class and add your own constructor. So long as you arent changing the base methods you are testing this should work.

For example:

public class MyBar:Bar
{
    // internal constructor
    public MyBar(object throwaway)
    {
       //call base constructor if necessary
    };
    public int Id { get; }
    public string Name { get; }
    public Foo Foo { get; }
}
2012-04-05 16:45
by StingyJack
The classes aren't sealed, but I can't derive from it either. The base class constructor would be internal to another module - PaulH 2012-04-05 16:53
Not sure I see that this doesn't solve your problem. You can derive from it. You can't call the internal constructor, but your mock can perform the same behavior (copy/paste from reflector or ildasm). If that isn't good enough, then you should be considering a different pattern for object construction that doesn't rely so much on concretions - StingyJack 2012-04-09 15:11
Also, this has some info http://www.rvenables.com/2009/08/instantiating-classes-with-internal-constructors - StingyJack 2012-04-09 15:15
Ads