我试图找到一种方法来伪造从另一个方法中调用的方法的结果。
我有一个" LoadData"方法,该方法调用一个单独的帮助程序以获取一些数据,然后将其转换(我有兴趣测试转换后的结果)。
所以我有这样的代码:
1 2 3 4 5 6
| public class MyClass(){
public void LoadData(){
SomeProperty = Helper.GetSomeData();
}
public object SomeProperty {get;set;}
} |
我想从Helper.GetSomeData()方法获得已知结果。我可以使用模拟框架(我对Rhino Mocks的经验有限,但可以接受任何东西)来强制达到预期的结果吗?如果是这样,如何?
* Edit-是的,正如我所料,我无法实现我想要的hack,我必须找到一种更好的方式来设置数据。
您在那里遇到问题。我不知道这是否是代码的简化方案,但是如果以这种方式使用Helper类,则您的代码不可测试。首先,直接使用Helper类,因此您不能用模拟代替它。其次,您要调用静态方法。我不了解C#,但是在Java中,您无法覆盖静态方法。
您必须进行一些重构,才能使用伪GetSomeData()方法注入模拟对象。
在此简化的代码版本中,很难给您一个直接的答案。您有一些选择:
-
为Helper类创建一个接口,并为客户端提供一种将Helper实现注入MyClass类的方法。但是,如果Helper实际上只是一个实用程序类,那就没有多大意义了。
-
在MyClass中创建一个名为getSomeData的受保护方法,并使其仅调用Helper.LoadSomeData。然后,使用getSomeData替换对LoadData中的Helper.LoadSomeData的调用。现在,您可以模拟getSomeData方法以返回虚拟值。
提防只创建一个到Helper类的接口并通过方法注入它。这可以公开实现细节。为什么客户端应该提供实用程序类的实现来调用简单操作?这将增加MyClass客户端的复杂性。
我建议将您拥有的东西转换为如下形式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| 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(控制反转)容器。如果这对您来说是新手,我建议您首先关注为什么所有这些麻烦都值得一去的基本原理(恕我直言)。
您的单元测试将类似于以下伪代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| 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
据我所知,您应该为Helper对象创建一个接口或一个基本抽象类。使用Rhino Mocks,您可以返回所需的值。
或者,您可以为LoadData添加一个重载,该重载接受通常从Helper对象检索的数据作为参数。这甚至可能更容易。
我会尝试这样的事情:
1 2 3 4
| public class MyClass(){
public void LoadData(IHelper helper){
SomeProperty = helper.GetSomeData();
} |
这样,您可以使用例如最小起订量来模拟助手类。
是的,模拟框架正是您所需要的。您可以记录/安排希望某些模拟/存根类返回的方式。
Rhino Mocks,Typemock和Moq都是不错的选择。
Steven Walther最初使用Rhino Mocks的文章对我有很大帮助。