RhinoMock - 使用一个真实的对象,但存根一个单一的方法

RhinoMock - Use a real object, but stub a single method

我正在针对依赖于 IFoo 的 MVC 控制器编写单元测试。 Foo(实现)有一种方法我想存根,但我想保持另一种不变。如何使用 RhinoMock?

进行设置

Foo 有几个依赖项,我更喜欢 而不是 来模拟以节省编写额外的代码行和使我的测试混乱。

富:

public interface IFoo{
    int Method1();
    int Method2();
}

public class Foo : IFoo{
   //lot's of dependencies
   public Foo(IBar bar, IBaz baz, IWhosebug so){}
}

测试:

[Test]
public void What_I_Have_So_Far(){
    //arrange
    //load the real IFoo from Ninject (DI)
    var mockFoo = new Ninject.Kernel(new MyExampleModule())
                    .Get<IFoo>();

    //I want this test to use the real Method1, but not Method2
    //so stub Method2
    mockFoo
       .Stub(x => x.Method2()) //<---- blows up here
       .Returns(42);

    //act
    var controllerUnderTest = new Controller(mockFoo);

错误:

使用这种方法,RhinoMock 抛出异常:

System.InvalidOperationException : The object 'MyApplication.MyExampleModule' is not a mocked object.

问题:

我如何存根 method2

我知道我可以通过 MockRepository.GenerateMock 创建 IFoo 作为模拟,但是我必须复制 Method1 的真实实现。


更新:

Brad 和 Jimmy 的解决方案都可以很好地工作,我选择 Brad 的只是因为它需要编写的代码更少。

然而,在进一步研究之后,看起来我需要的是一个 AutoMocker。 StructureMap and Moq, but not RhinoMocks: https://github.com/RhinoMocks/RhinoMocks/issues/3

有一条缝

你必须反过来做。创建模拟 IFoo 并将 某些 调用重定向到真实 IFoo(这必须通过 WhenCalled 扩展名完成):

var realFoo = new Ninject.Kernel(new MyExampleModule()).Get<IFoo>();
var mockFoo = MockRepository.GenerateStub<IFoo>();

mockFoo.Stub(f => f.Method2()).Return(42);
mockFoo.Stub(f => f.Method1())
   .WhenCalled(invocation =>
   {
       invocation.ReturnValue = realFoo.Method2();
   })
   .Return(whateverValue);

最后的 Return 是必需的,即使我们在几行之前覆盖了它。否则 Rhino 会抛出异常。

只要付出最少的 "invasive" 努力,我就会考虑使用适配器模式。所以,我们有我们原来的界面和具体 class:

public interface IFoo
{
    void Method1();
    void Method2();
}

public class Foo : IFoo
{
    public void Method1()
    {
        Console.WriteLine("I am Method1 of Foo");
    }
    public void Method2()
    {
        Console.WriteLine("I am Method2 of Foo");
    }
}

以及用于单元测试的单个实现:

public class FooAdapter : IFoo
{
    private readonly IFoo foo;

    public FooAdapter(IFoo foo)
    {
        this.foo = foo;
    }

    public void Method1()
    {
        this.foo.Method1();
    }

    public void Method2()
    {
        Console.WriteLine("I am Method2 of FooAdapter");
    }
}

class 可能是单元测试 class 的局部,简单定义为:

IFoo foo = new FooAdapter(kernel.Get<IFoo>());

我同意你的看法,改变你的基本代码以满足单元测试不是正确的道路。单元测试应该随便放在上面,永远不要反向插入到它们所针对的代码中。

However, after researching this a bit further, it looks like what I need is an AutoMocker. There seams to be one for StructureMap and Moq, but not RhinoMocks: https://github.com/RhinoMocks/RhinoMocks/issues/3

我没有深入研究,但我知道 CodePlex 上的 AutoMock 项目。也许这就是答案?

特征

  • 通过基于构造函数的依赖项注入,为任何依赖于一组依赖项的被测class动态创建mocks/stubs。
  • 目前支持 RhinoMocks。

https://automock.codeplex.com/