通过公共方法进行测试时开始发臭(示例)

有关测试公共方法文章中,我谈到了私有类逻辑的单元测试。 我认为重做论文是值得的,因为我认为大多数人都认为我们正在谈论测试私有方法,尽管这是关于私有逻辑的。 在本文中,我想用一个实际的例子来说明主要论文。 在凯特的例子下进行一点分析。

股票例子


为了说明问题,我举了一个例子。 他的想法来自一个真实的项目。 理想情况下,就像有人可能会注意到的那样,该班级的写作方式必须有所不同。 但是现在没有人会重写它(或重构),因为 这将花费大量时间来编辑和测试有效的方法。 因此,它不会被批准。 在这种情况下,您需要更改代码并且更改必须正确。 因此,这是代码。 重要的逻辑集中在ProcessMessage方法中。 目的:在消息处理中引入新的业务逻辑标志。 该标志具有非平凡的逻辑。

您将如何实现和测试标志?

using System; using System.Threading.Tasks; using System.Threading; using System.Messaging; namespace PublicMethodNottrivialSample { public class MessageProcessorService { private object _lockObject = new object(); private CancellationTokenSource _cancellationSource; private Action _receiveMessage; private int _listenerThreads = 5; public void Start() { lock (_lockObject) { _cancellationSource = new CancellationTokenSource(); Task.Factory.StartNew(() => InitializeReceiveLoop(_cancellationSource.Token), _cancellationSource.Token); } } private void InitializeReceiveLoop(CancellationToken cancellationToken) { _receiveMessage = () => { while (!cancellationToken.IsCancellationRequested) { using (MessageQueueTransaction msgTx = new MessageQueueTransaction()) { try { msgTx.Begin(); //   MessageQueue queue = new MessageQueue(); Message message = queue.Receive(msgTx); //    ,    if (!cancellationToken.IsCancellationRequested) { ProcessMessage(message); } msgTx.Commit(); } catch (Exception ex) { // some logging } } } }; for (int n = 0; n < _listenerThreads; n++) { StartProcessingLoop(cancellationToken); } } private void ProcessMessage(Message message) { //   ,     } private void StartProcessingLoop(CancellationToken cancellationToken) { Task.Factory.StartNew(_receiveMessage, cancellationToken); } } } 

该类显示唯一的公共方法是Start()。 如果更改签名,则可以对其进行测试,但是在这种情况下,公共接口将更改。 另外,您将需要更改几种方法来返回正在运行的线程,然后等待它们在测试中完成。

但是,正如我们回想的那样,该要求仅涉及在处理消息的过程中标志的实现,并不意味着接收消息的机制的操作发生了变化。 因此,无论由谁进行更改,我都希望只有一种方法可以解决,并且单元测试仅与可变逻辑有关。 为了实现这一点,很难将其保留在原则的框架内:测试将变得不平凡,这将导致拒绝编写它或复杂的代码。 这就是通过公共方法开始测试的方式。 然后有人会在情感上写下:“ TDD时间长了”,“客户不付款”或“测试效果不佳”。

通常,有必要测试这样的代码,但不要使用单元测试,而要使用集成测试。

“您跳棋,还是去”


当然,有必要为更改后的逻辑编写单元测试。 我认为,在这种情况下,难题是选择编写测试的最便宜方法,而不是有用的代码。 我的意思是,无论您做什么:重构公共方法或其他解决方案-您这样做都是为了编写测试,而不是满足客户任务的要求。 在这种情况下,建议评估成本和效果。 除了上述具有重构的解决方案之外,还有几种替代解决方案。 我带来了一切,下面我们将讨论优点和缺点:

  1. 可以测试私有方法
  2. 该方法可以公开吗
  3. 可以设为内部
  4. 可以作为公共方法放入单独的类中

如果我们将前三种方法与通过公共方法得出的解决方案进行比较,但所有这些方法都需要较少的人工成本(使用私人公司不是事实)。 而且,它们实际上都是相同的解决方案,但在样式上略有不同。 因为 结果将以任何方式获得,因此,我认为,选择其中一种解决方案的选择不应基于该语言的技术能力,而应基于您希望向其他开发人员展示的内容:

  1. 如果该方法可以在解决方案中的任何位置运行,并且是类行为的一部分,则将其公开,并将其拉入类接口;
  2. 如果该方法可以在任何其他类中使用,但只能在此程序集中使用,则在内部进行
  3. 如果该方法只能在主类内部使用(可以从任何其他方法调用),则将其设置为internal

可以使用InternalsVisibleTo属性使内部方法对测试程序集可见,并照常进行调用。 这种方法使编写测试变得更加容易,结果将是相同的。

测试ProcessMessage


让我们回到任务。 按照上述方法,我将进行一些更改:

  • 将使ProcessMessage公开
  • 会为标志逻辑创建一个新的受保护的内部方法(例如GetFlagValue)
  • 将在所有情况下为GetFlagValue编写测试
  • 我将为ProcessMessage编写测试,以确保正确使用GetFlagValue:参数正确传递并且确实被使用

我要澄清的是,只要我在GetFlagValue单元测试中对这些案例进行了测试,就不会为所有在ProcessMessage方法中使用GetFlagValue的案例编写单元测试。 在未发现的情况下,必须添加它们。 在这种情况下,主要方法仍然是:

  • 所有案例都包含在GetFlagValue单元测试中
  • ProcessMessage单元测试验证是否正确使用了flag方法

我相信,据此,您只能为ProcessMessage编写一个单元测试,而对于GetFlagValue编写多个。

这样的东西。 您的意见

Source: https://habr.com/ru/post/zh-CN420567/


All Articles