Rhino模拟单元测试(Parent类不包含带0参数的构造函数)

时间:2012-02-29 12:27:04

标签: c# unit-testing mocking rhino-mocks

Rhino Mock专家请帮助。

下面的犀牛模拟示例工作正常。

internal class TestClass
{
    private ITestInterface testInterface;

    public TestClass(ITestInterface testInterface)
    { 
       this.testInterface = testInterface;
    }

    public bool Method1(int x)
    {
        testInterface.Method1(x);
        return true;
    }

}

[TestClass]
public class UnitTest2
{
    [TestMethod]
    public void TestMethod1()
    {
        ITestInterface mockProxy = MockRepository.GenerateMock<ITestInterface>();

        TestClass tc = new TestClass(mockProxy);
        bool result = tc.Method1(5);           

        Assert.IsTrue(result);


        mockProxy.AssertWasCalled(x => x.Method1(5));
    }
}

我面临的问题如上所述,我需要在所有子类中创建一个构造函数,如下所示: - 。

 internal class testclass1 : TestClass
    {
        protected testclass1(ITestInterface testInterface) : base(testInterface)
        {
        }
    }

是否有任何解决方法,因为我有大约50个儿童课程?

1 个答案:

答案 0 :(得分:4)

正如我对你的问题的评论中提到的,这不是由模拟或单元测试引起的问题。这是使用构造函数注入的自然结果(即提供Dependencies作为构造函数参数)。另一种方法是使用属性注入,即创建一个可写属性,通过它可以设置ITestInterface对象,如下所示:

public class TestClass {
    public ITestInterface FooBar {protected get; set; }
}

这样,所有派生类都可以访问FooBar而无需创建自己的构造函数来将ITestInterface传递给基类。

然而,这还有其他后果:构造函数注入通常表示强制依赖。如果没有这种依赖关系,该课程将无效。但是,属性注入通常用于表示可选的依赖项或覆盖默认行为的方法。但是,这种区别并不是一成不变的,如果你真的有几十个来自TestClass构造函数注入的类可能成为维护的真正负担。

如果一个对象需要构造函数参数,那么用户必须传递必需的依赖项。使用属性注入,更容易忘记设置属性以正确设置对象(如果需要依赖项)。此外,使用构造函数注入时,您只需要检查依赖项是否已提供一次(在构造函数中),而使用属性注入时,您更有可能需要在每次使用时检查依赖项的有效性。

同样,如果使用构造函数注入导致维护噩梦,因为你有许多子类,尽管我之前写过,但属性注入仍然是一个可行的选择。 您还可以使用如下的初始化方法表达所需的属性依赖项:

public static class TestClassInitializer{
    public T Initialize<T>(this T t, ITestInterface dependency) where T:TestClass{
        t.FooBar = dependency;
        return t;
    }
}

如果你可以强迫自己构建这样的对象:

var tc = new DerivedTestClass().Initialize(mockTestInterface);

您可以在一个点上修改所需的依赖项。如果您现在需要第二个依赖项,只需更新签名以包含第二个依赖项

public static class TestClassInitializer{
    public T Initialize<T>(this T t, ITestInterface dependency, IOtherDependency other) where T:TestClass{
        t.FooBar = dependency;
        t.OtherDependency = other;
        return t;
    }
}

所有调用Initialize的地方都会在编译时中断。这是一件好事(!),因为您需要来提供所需的依赖项。但是,需要更新这些位置,而不是50个派生类以添加其他依赖项。如果你已经使用了构造函数注入,那么你需要更新所有50个类所有你实例化它们的地方。

另外:如果您知道派生类只有默认构造函数,则可以为所有类创建工厂方法:

public static class TestClassFactory{
    public static T Create<T>(ITestInterface dep1, ITestInterface dep2) where T :TestClass, new(){
        return new T{FooBar = dep1, OtherDependency = dep2};
    }
}

现在可以使用new DerivedTestClass().Initialize(...)代替TestClassFactory.Create<DerivedTestClass>(mockedDep1, mockedDep2);。只要TestClassFactory位于同一个程序集中(或者可以通过InternalsVisibleTo访问内部构造函数的程序集中),就可以将实际的构造函数(protected)设置为内部。

如果您使用依赖注入框架/ IoC容器,这一切都不是问题,因为一旦正确设置,它不会忘记提供依赖项。