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
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://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.
Cannot convert type PrivateObject to Foo
error when I try to cast between the two - PaulH 2012-04-05 16:55
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.
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; }
}
Foo
andBar
displays implicit constructors. Unless you specifically haveinternal Foo() { }
, the compiler will generate a parameterless constructor with the same visibility as the class - Jim Schubert 2012-04-05 16:27