使用第三方程序集中的对象进行单元测试

时间:2012-04-05 16:22:45

标签: c# unit-testing

我有一个Visual Studio 2008 C#.NET 3.5项目,我正在实施单元测试,但我遇到了一个问题。我的代码引用了第三方程序集,它使用内部构造函数实现对象。

例如:

// 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();
}

我想对其进行单元测试的一种方法是:

// 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
}

并按照以下方式进行测试:

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");
}

但是,我无法创建Foo类型或Bar类型的对象。那么,我该如何对我的方法进行单元测试呢?

由于

3 个答案:

答案 0 :(得分:3)

对于单元测试,您可以使用PrivateObject类(名称空间Microsoft.VisualStudio.TestTools.UnitTesting)来创建具有私有构造函数的对象,甚至可以测试私有方法。

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

以下是一个例子:

[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;
    }
}

它以与Jim的建议相同的方式使用反射,但PrivateObject类封装了使用私有构造函数创建实例的所有工作。

答案 1 :(得分:1)

您可以使用反射来创建具有非公共构造函数的对象。请参阅this question on SO

以上是来自上述链接的Ani解决方案:

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将根据您提供的参数找到正确的构造函数。

答案 2 :(得分:0)

如果类没有密封/不可接受,那么您可以从测试目标类派生一个“模拟”类并添加自己的构造函数。只要你没有改变你正在测试的基本方法,这应该可行。

例如:

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; }
}