模拟方法结果
我试图找到一种方法来伪造从另一个方法中调用的方法的结果。
我有一个" LoadData"方法,该方法调用一个单独的助手来获取一些数据,然后它将对其进行转换(我对测试转换后的结果很感兴趣)。
所以我有这样的代码:
public class MyClass(){ public void LoadData(){ SomeProperty = Helper.GetSomeData(); } public object SomeProperty {get;set;} }
我想从Helper.GetSomeData()方法获得已知结果。我可以使用模拟框架(我对Rhino Mocks的经验有限,但可以接受任何东西)来强制达到预期的结果吗?如果是这样,怎么办?
*按预期编辑,是无法实现所需的技巧,因此,我必须找到一种更好的方法来设置数据。
解决方案
是的,模拟框架正是我们想要的。我们可以记录/安排如何让某些模拟/存根类返回。
Rhino Mocks,Typemock和Moq都是执行此操作的不错选择。
当我第一次开始与Rhino Mocks玩游戏时,Steven Walther的使用Rhino Mocks的帖子对我有很大帮助。
据我所知,我们应该为Helper对象创建一个接口或者一个基本抽象类。使用Rhino Mocks,我们可以返回所需的值。
或者,我们可以为LoadData添加一个重载,该重载接受通常从Helper对象检索的数据作为参数。这甚至可能更容易。
你那里有问题。我不知道这是否是代码的简化方案,但是如果以这种方式使用Helper类,则代码不可测试。首先,直接使用Helper类,因此我们不能用模拟代替它。其次,我们要调用静态方法。我不了解C#,但是在Java中,我们无法覆盖静态方法。
我们必须进行一些重构,才能使用虚拟GetSomeData()方法注入模拟对象。
在这种简化的代码版本中,很难给我们一个直接的答案。我们有一些选择:
- 为Helper类创建一个接口,并为客户端提供一种将Helper实现注入MyClass类的方法。但是,如果Helper实际上只是一个实用程序类,那就没有多大意义了。
- 在MyClass中创建一个名为getSomeData的受保护方法,并使其仅调用Helper.LoadSomeData。然后,使用getSomeData替换对LoadData中的Helper.LoadSomeData的调用。现在,我们可以模拟getSomeData方法以返回虚拟值。
注意不要简单地创建一个与Helper类的接口并通过method注入它。这可以公开实现细节。为什么客户端应该提供实用程序类的实现来调用简单操作?这将增加MyClass客户端的复杂性。
我会尝试这样的事情:
public class MyClass(){ public void LoadData(IHelper helper){ SomeProperty = helper.GetSomeData(); }
这样,我们可以使用例如最小起订量来模拟帮助程序类。
我建议将我们拥有的东西转换成这样的东西:
public class MyClass() { private IHelper _helper; public MyClass() { //Default constructor normal code would use. this._helper = new Helper(); } public MyClass(IHelper helper) { if(helper == null) { throw new NullException(); //I forget the exact name but you get my drift ;) } this._helper = helper; } public void LoadData() { SomeProperty = this._helper.GetSomeData(); } public object SomeProperty {get;set;} }
现在,类支持所谓的依赖注入。这允许我们注入帮助程序类的实现,并确保类只需要依赖于接口。进行模拟时,我们只需创建一个使用IHelper接口的模拟并将其传递给构造函数,类就将使用它,就好像它是真正的Helper类一样。
现在,如果我们不愿意将Helper类用作静态类,那么我建议我们使用代理/适配器模式,并将静态类包装到另一个支持IHelper接口的类中(也需要创建)。
如果我们想进一步采取措施,则可以从修订的类中完全删除默认的Helper实现,并使用IoC(控制反转)容器。如果这对我们来说是新手,我建议我们首先关注为什么所有这些额外麻烦都值得的基本原理(恕我直言)。
单元测试将类似于以下伪代码:
public Amazing_Mocking_Test() { //Mock object setup MockObject mockery = new MockObject(); IHelper myMock = (IHelper)mockery.createMockObject<IHelper>(); mockery.On(myMock).Expect("GetSomeData").WithNoArguments().Return(Anything); //The actual test MyClass testClass = new MyClass(myMock); testClass.LoadData(); //Ensure the mock had all of it's expectations met. mockery.VerifyExpectations(); }
如有任何疑问,请随时发表评论。 (顺便说一句,我不知道这段代码是否可以正常工作,而我只是在浏览器中输入了这些代码)。
我们可能需要研究Typemock隔离器,它可以"伪造"方法调用,而不会强迫我们重构代码。
我是该公司的一名开发人员,但如果我们不想选择更改设计(或者出于测试目的而被迫不更改它),则该解决方案是可行的
它在www.Typemock.com
罗伊
博客:ISerializable.com