RhinoMock -使用一个真实的对象,但是存根一个方法
本文关键字:一个 存根 方法 对象 RhinoMock 真实 | 更新日期: 2023-09-27 17:53:10
我正在编写一个针对MVC控制器的单元测试,该控制器依赖于IFoo
。Foo
(实现)有一个方法我想要存根,但我想保留另一个完整的。我如何使用RhinoMock
设置这个?
Foo
有几个依赖项,我更喜欢而不是来模拟,以节省编写额外的代码行和混乱我的测试。
Foo:
public interface IFoo{
int Method1();
int Method2();
}
public class Foo : IFoo{
//lot's of dependencies
public Foo(IBar bar, IBaz baz, IStackOverflow 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抛出一个异常:
系统。InvalidOperationException:对象的MyApplication。MyExampleModule'不是一个模拟对象。
问题:
如何存根method2
?
我知道我可以通过MockRepository.GenerateMock
创建IFoo
作为模拟,但是然后我必须复制Method1
的实际实现。
更新:
Brad和Jimmy的解决方案看起来都一样好,我选择Brad的解决方案只是因为要写的代码更少。
然而,在进一步研究之后,看起来我需要的是一个AutoMocker。似乎有一个StructureMap和Moq,但不是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会抛出异常。
如果需要最少的"侵入性"工作,我会考虑使用适配器模式。因此,我们有了原始接口和具体类:
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");
}
}
这个类可以是单元测试类的局部类,可以简单地定义为:
IFoo foo = new FooAdapter(kernel.Get<IFoo>());
我同意你的观点,改变你的基本代码来满足单元测试不是正确的途径。单元测试应该随意地放在最上面,永远不要反向探测到它们所针对的代码。
然而,在进一步研究之后,它看起来像我需要是一个自动开关。StructureMap和Moq应该有一个,但不是RhinoMocks: https://github.com/RhinoMocks/RhinoMocks/issues/3
我没有深入研究它,但我知道CodePlex上的AutoMock项目。也许这就是答案?
- 通过基于依赖注入的构造函数为任何依赖于一组依赖的被测类动态地创建模拟/存根。
- 目前支持RhinoMocks。