使用委托作为“工厂方法”用于 C# 中的单元测试

发布于 2024-12-19 11:42:22 字数 580 浏览 6 评论 0原文

问题

让我们“画出”一幅情况图:

  • 我有一个 SUT。 (有一件好事:P)
  • 我可以在我的 SUT 上注入一些依赖项。
  • 在一个方法中,我做了一个:new OtherClass(ParametersObtainedFromDependancies)。
  • 我想在我的 SUT 中对该方法进行单元测试,而不处理创建模拟对象的复杂性,这对于 OtherClass 的正常工作有意义。这意味着要测试 OtherClass。

我的解决方案是使用委托的工厂方法:

public Func<OtherClass,Param1Type> GetOtherClass = 
       (param1) => new OtherClass(param1);

缺点:它是公共的。您可以将其视为可选的依赖项,如果需要,您可以覆盖它。但无论如何,公共气味。

好处是:我不需要创建一个 MyTestSUT 来重写此方法,甚至不需要在 SUT 上使用模拟来重写该方法。

问题

有更好的解决办法吗?这是正确的吗?

The problem

Let's "draw" a picture of the situation:

  • I have a SUT. (a good thing to have :P )
  • I can inject some dependencies on my SUT.
  • In a method I do a: new OtherClass(ParametersObtainedFromDependancies).
  • I want to unit test that method in my SUT without handling the complexity of creating mock object that make sense for OtherClass to work correctly. This will imply to test OtherClass.

My solution to this was a factory method using a delegate:

public Func<OtherClass,Param1Type> GetOtherClass = 
       (param1) => new OtherClass(param1);

The bad: it's public. You could think on it as an optional dependency that you can override if need. But anyway, public smells.

The good: I don't need to create a MyTestSUT that will override this method, or even use a mock on the SUT to override that method.

Question

Is there any better solution? Is this correct?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

記憶穿過時間隧道 2024-12-26 11:42:22

为什么你把它设为公共领域?听起来它基本上是一个像任何其他依赖项一样的依赖项:当您给它一些其他值时,您想要一些能够为您提供 OtherClass 的东西。为什么不像其他依赖项一样将其作为构造函数参数(或者您正在执行其余的依赖项注入)?

如果您愿意,您始终可以提供不带此参数的构造函数重载,该重载将委托给更长的参数。这就是我通常提供“默认”依赖实现的方式。

换句话说:这在逻辑上与(例如)IAuthenticator 接口有何不同,后者在提供用户名和密码时将返回 UserProfile ?您碰巧使用委托作为避免声明接口的捷径,但这只是一个细节 IMO。您可能会发现,随着时间的推移,您实际上希望在“提供”部分投入更多智能 - 例如缓存。

Why have you made it a public field? It sounds like it's basically a dependency like any other: you want something which will provide you with an OtherClass when you give it some other values. Why not just make it a constructor parameter like other dependencies (or however you're doing the rest of your dependency injection)?

You can always provide a constructor overload without this parameter which delegates to the longer one, if you want to. That's how I normally provide "default" dependency implementations.

To put it another way: how is this logically different from (say) an IAuthenticator interface which, when provided with a username and password will return a UserProfile? You happen to be using a delegate as a short-cut to avoid declaring the interface, but that's just a detail IMO. You may find that as time goes on, you actually want to put more smarts in the "providing" part - e.g. caching.

晨光如昨 2024-12-26 11:42:22

听起来您想使用类似于 AutoFixture 的东西,它的 AutoMoq 自定义已启用。
如果您愿意,这将使您能够模拟 SUT 中的全部或部分公共字段以及其他构造函数参数。

It sounds like you want to use something similar to AutoFixture with it's AutoMoq customization enabled.
This will enable you to mock all or some of your public fields in your SUT along with your other constructor parameters if you wish.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文