9

我试图找到一种方法来伪造从另一个方法中调用的方法的结果。

我有一个“LoadData”方法,它调用一个单独的助手来获取一些数据,然后它会转换它(我有兴趣测试转换后的结果)。

所以我有这样的代码:

public class MyClass(){
  public void LoadData(){
    SomeProperty = Helper.GetSomeData();
 }
 public object SomeProperty {get;set;}
}

我想从 Helper.GetSomeData() 方法得到一个已知的结果。我可以使用模拟框架(我对 Rhino Mocks 的经验相当有限,但对任何事情都持开放态度)来强制实现预期结果吗?如果是这样,怎么做?

*编辑 - 是的,正如预期的那样,我无法实现我想要的破解,我必须找到一种更好的方法来设置数据。

4

6 回答 6

10

你那里有问题。我不知道这是否是您的代码的简化方案,但如果以这种方式使用 Helper 类,那么您的代码是不可测试的。首先,Helper 类是直接使用的,所以不能用 mock 代替。其次,您正在调用静态方法。我不了解 C#,但在 Java 中你不能覆盖静态方法

您必须进行一些重构才能使用虚拟 GetSomeData() 方法注入模拟对象。

在你的代码的这个简化版本中很难给你一个直接的答案。你有一些选择:

  • 为 Helper 类创建一个接口,并为客户端提供一种将 Helper 实现注入 MyClass 类的方法。但是,如果 Helper 真的只是一个实用程序类,那它就没有多大意义了。
  • 在 MyClass 中创建一个名为 getSomeData 的受保护方法,并使其仅调用 Helper.LoadSomeData。然后将 LoadData 中对 Helper.LoadSomeData 的调用替换为 for getSomeData。现在您可以模拟 getSomeData 方法以返回虚拟值。

当心简单地创建一个到 Helper 类的接口并通过方法注入它。这可以暴露实现细节。为什么客户端应该提供一个实用程序类的实现来调用一个简单的操作?这将增加 MyClass 客户端的复杂性。

于 2008-09-18T07:19:56.967 回答
8

我建议将你所拥有的东西转换成这样的东西:

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();
}

如果您有任何问题,请随时发表评论。(顺便说一句,我不知道这段代码是否都能正常工作,我只是在浏览器中输入了它,我主要是在说明概念)。

于 2008-09-18T07:35:52.560 回答
2

据我所知,您应该为 Helper 对象创建一个接口或一个基本抽象类。使用 Rhino Mocks,您可以返回您想要的值。

或者,您可以为 LoadData 添加一个重载,该重载接受您通常从 Helper 对象检索的数据作为参数。这甚至可能更容易。

于 2008-09-18T07:16:10.347 回答
2

您可能想研究 Typemock Isolator,它可以“伪造”方法调用,而不会强迫您重构代码。我是该公司的开发人员,但如果您想选择不更改您的设计(或为了可测试性而被迫不更改它),该解决方案是可行的,它位于 www.Typemock.com

罗伊博客:ISerializable.com

于 2008-09-19T01:22:50.640 回答
0

是的,模拟框架正是您正在寻找的。您可以记录/安排您希望某些模拟/存根类返回的方式。

Rhino Mocks、Typemock 和 Moq 都是很好的选择。

当我第一次开始使用 Rhino Mocks 时,Steven Walther 关于使用 Rhino Mocks 的帖子对我帮助很大。

于 2008-09-18T07:15:48.350 回答
0

我会尝试这样的事情:

public class MyClass(){
  public void LoadData(IHelper helper){
    SomeProperty = helper.GetSomeData();
 }

通过这种方式,您可以使用例如 MOQ 来模拟帮助程序类。

于 2008-09-18T07:20:51.787 回答