【问题标题】:Mocking problem, public function calling private function模拟问题,公共函数调用私有函数
【发布时间】:2009-06-24 10:20:55
【问题描述】:

我遇到了一个嘲弄问题。我知道只有公共方法应该被嘲笑。但是当在公共方法中调用了私有方法,而这个私有方法是从文件中获取数据时,我该怎么办呢?我想模拟这个私有方法,以便继续测试公共方法。

我可以将其公开以进行测试,但这没有任何意义,因为它是私有的。我也可以将所有内容移到另一个类中并在那里公开函数,但是对主类中对象的引用应该是私有的。

我正在使用 Rhino Mocks。

感谢您的帮助:)

【问题讨论】:

  • 那么你有一个设计问题,而不是模拟问题。

标签: mocking rhino-mocks private-functions


【解决方案1】:

如果您将读取文件的功能放入一个类中,例如

FileReader : IFileReader

然后将 IFileReader 作为 arg 传递给构造函数。然后你可以模拟它

【讨论】:

  • 我投票赞成你的答案,因为它有效,但我看到以下缺点,欢迎提供反馈: - 单个 IFileReader 必须在对象的生命周期内持续存在,当它可能只用于单个函数时- 它使对象比它必须的更基于状态。 IFileReader 需要在后续函数调用之间重置,以确保函数每次调用时的行为方式都相同,这会带来额外的复杂性。 - 这也造成了一次只能使用一个 IFileReader 的限制,除非有办法复制对象。
  • 你是对的,但是从文件中读取就像从存储库中获取一样,我认为应该将功能分开。也许如果你像 Windsor 那样使用依赖注入,它们可能已经内置了某种缓存。不过我真的不确定。
  • 感谢您的回答!我看到它可以工作,但我不希望它作为构造函数的参数,因为 CiscoIPPhone 也提到了缺点。对于我在私有方法中用于外部数据的每个对象,我都需要这样做,对于其他私有方法,我需要更多。当人们不得不在公共方法中模拟私有方法调用时,他们通常会做什么?
  • 我仍然认为应该始终分离获取功能是正确的。是的,它使测试更加复杂,但是您确实在测试该对象的功能,例如控制器。如果您开始使用数据库而不是文件,那么您将只是 IFileReader 的具体实现(现在是一个错误的名称选择),以及一个从 db 获取的类。依赖注入框架可以处理在运行时传递正确的对象,但这显然无助于您的测试问题
【解决方案2】:

拉出文件依赖,在构造函数中传入一个IFileSomething接口。 然后模拟 IFileSomething 并对其设置期望。

【讨论】:

    【解决方案3】:

    其他建议的替代方法是使您的代码模板化并接收 FileReader 类型,该类型可在您的类的整个方法中使用。

    template <class FileReader>
    class SomeClass
    {
      private: void doSomething()
      {
        FileReader fileReader;
        // Do something
      }
    };
    

    另一种方法是将方法传递给返回 FileReader 实现的 SomeClass 构造函数。这可以在整个类中使用,类似于使用模板的方式,但是这样做您仍然会从 IFileReader 派生 MockFileReader。

    其中任何一个的问题是无法在 FileReader 上执行单元测试,因为 SomeClass 无法访问它。

    n.b.上面的代码是 C++,但我知道这两种方法都可以用 C#。

    【讨论】:

      【解决方案4】:

      无论这是服务还是商业实体,请确保您同意自己的看法。

      服务通常被认为是它使用一组业务实体来完成某些任务,并且它本身并不维护持久数据。

      业务实体是您的问题域中的一个单元,您希望在概念上坚持下去。

      如果你发现它是一个服务,你应该在构建服务时以某种方式注入依赖。 通常它可以有一个这样的构造函数: 公共我的服务(IFileObject)

      当从 main 构造时: var service = new MyService(MyRealFile)

      从测试设置构造时: var service = new MyService(MyMockedFiled)

      如果您发现您正在测试的实际上是一个业务实体,那么您应该避免给该实体一个依赖项。您通常会后退一步,在您自己和业务实体之间构建一个服务类。该服务明确地向业务实体提供所有必要的数据。在您的情况下,这意味着该服务为实体提供了它应该通过读取文件来学习的任何内容。

      因此,服务依赖于文件系统,甚至可以使用另一个(专用)文件读取器业务实体来读取文件。如果您在系统的其他地方使用业务实体,您将永远不会希望进行恶意依赖调用。他们的代码变成了上下文绑定的,这是你想要避免的。业务实体应快速、离散、明确且响应迅速。

      您的单元测试应针对服务,该服务可能会通过注入接收依赖项。如果您发现您正在对业务实体进行单元测试,那么您缺少服务级别。

      【讨论】:

        【解决方案5】:

        感谢您的回答,我的 cookie 被删除了。我使用了第一个答案并在代码中进行了一些更改。效果很好:)

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2012-12-06
          • 1970-01-01
          • 2015-04-02
          • 2013-05-01
          • 1970-01-01
          • 2012-05-29
          • 1970-01-01
          • 2017-06-08
          相关资源
          最近更新 更多