唯一的逻辑是警卫的单元测试方法是否有用?


12

说我有一个这样的方法:

public void OrderNewWidget(Widget widget)
{
   if ((widget.PartNumber > 0) && (widget.PartAvailable))
   {
        WigdetOrderingService.OrderNewWidgetAsync(widget.PartNumber);
   }
}

我的代码中有几种这样的方法(异步Web服务调用的前半部分)。

我正在辩论将它们包含在单元测试中是否有用。是的,这里有逻辑,但这只是保护逻辑。(这意味着在允许进行Web服务调用之前,请确保已拥有所需的东西。)

我有一部分说“确保可以对它们进行单元测试,但这不值得花时间”(我在一个已经落后于进度的项目中)。

但是我的另一面说,如果您不对它们进行单元测试,并且有人更换了Guards,那么可能会有问题。

但是我的第一部分说,如果有人更换了警卫,那么您将为他们做更多的工作(因为现在他们必须更换警卫和警卫的单元测试)。

例如,如果我的服务承担检查Widget可用性的责任,那么我可能不再想要那个守护者。如果正在单元测试中,我现在必须更改两个位置。

我在两种方式上都有利弊。所以我想问一下别人做了什么。


17
您不会为维护人员付出更多的努力。如果他们更改逻辑,则必须更改相应的单元测试。这就是它的工作原理。我没有看到您的缺点:如果您的单元测试不需要更改,那么它就不会测试任何东西,对吗?您可能还会质疑单元测试是否完全有用。
Andres F.

9
这不是主题,但是如果零件号为0或更小,或者零件不可用,我会更改逻辑以引发异常,因为我认为允许某人使用伪造的小部件,默默掩盖了另一个问题。
马修

2
@Matthew非常好。这个功能在于。命名告诉您它将要订购一些东西。然后它不会,但是您永远不会知道,除非您应用与内部相同的逻辑,否则将导致DRY错误。换句话说:如果将设计更改为更正确,则可能根本不会问这个问题。
stijn 2012年

2
but it is not worth the time" (I am on a project that is already behind schedule).我们是软件开发人员。我们唯一按计划进行的活动是去世的时间:)
maple_shaft

Answers:


26

我有一部分说“确保可以对它们进行单元测试,但这不值得花时间”(我在一个已经落后于进度的项目中)。

这是三个非常简短的测试。您花了很多时间问自己这个问题。

但是我的另一面说,如果您不对它们进行单元测试,并且有人更换了Guards,那么可能会有问题。

听这一边。

但是我的第一部分说,如果有人更换了警卫,那么您将为他们做更多的工作(因为现在他们必须更换警卫和警卫的单元测试)。

如果您的维护者是TDD螺母,那么对他们来说将变得更加困难。如果不进行任何相关更改或添加测试,我所做的任何更改都会导致我不得不认真思考。实际上,在进行更改之前,我可能会添加测试。

您的第一部分完全是错误的。轻拍第二部分的背面,然后停止思考。


尽管我也写了一个答案,但还是为简洁起见+1
Jimmy Hoffa 2012年

3
+1。是的,如果需求发生变化,则必须对某些测试进行调整。
Olivier Jacot-Descombes 2012年

尽管我喜欢您说的话,但是我的代码中有许多此类调用的实例(异步调用的前半部分)。因此,这不仅仅是3个单元测试。不过,如果这是“正确”的方法,那么我想完成它们。
瓦卡诺

@Vaccano:您必须编写的代码越多,没有测试的逻辑路径就越多,编写它们的必要性就越高。
pdr 2012年

9

如果保护逻辑和实际顺序是分开的方法,它将简化单元测试。

Widget班上

public bool IsReadyForOrdering { get { return PartNumber > 0 && PartAvailable; } }

或其他地方的等效方法

public bool IsWidgetReadyForOrdering(Widget widget)
{
    return widget.PartNumber > 0 && widget.PartAvailable;
}

订购方法

public void OrderNewWidget(Widget widget)
{
   if (IsWidgetReadyForOrdering(widget)) {
        WigdetOrderingService.OrderNewWidgetAsync(widget.PartNumber);
   }
}

现在测试IsWidgetReadyForOrdering变得容易了。不要再考虑很久了。测试一下!


1
哎呀,属性中的有状态逻辑。-1应该是一个方法,由于简单而应采用PartNumber,PartAvailable。如果不需要将其作为公共API的一部分而不是属性,则使其受到保护。.我也普遍不同意。该方法内部的保护逻辑非常好,在我看来更好,因为它是一个很小的方法,您只是污染了类以使已经很小的方法变得更小。在这种情况下,仅当它是重复逻辑时才将其添加到类中。
吉米·霍法

1
首先,这不能回答问题。其次,这是不正确的。您需要以任何一种方式编写三个测试。第三,它不必要地将实现细节公开给调用代码。
pdr 2012年

8
@JimmyHoffa:您在哪里看到该属性中的任何有状态逻辑?实际上,该示例显示了如何将状态更改逻辑(OrderNewWidget)与非状态更改逻辑(ReadyForOrdering)分开。我坚信即使您提取小函数也可以改善代码,并且不会使代码变得更糟。所以+1。
布朗

2
该方法OrderNewWidget可能位于之外的另一个类中Widget,因为它有一个Widget参数。由于该方法没有返回值,因此对其进行测试并不明显。您将必须插入WigdetOrderingService-mock来跟踪OrderNewWidgetAsync呼叫。
Olivier Jacot-Descombes 2012年

2
@JimmyHoffa:实际上,您在C#中对“无状态”的定义是“静态”。因此,您所说的是“属性不应访问对象的状态”。但是,即使是愚蠢的getter也会访问状态变量,因此这意味着“只写静态属性”-似乎没有多大意义。
Doc Brown

6

如果您没有时间进行单元测试,但有时间留给可靠的QA使用,请询问您是否可以抽出部分QA时间来编写单元测试,或者是否可以花一些QA时间不幸的是,无法避免的时间表会迫使您做出让步或使自己丧命,我通常建议第一种选择,因为第二种选择会导致您无法支持/在整个期限内正确维护系统。

就是说,这是关于测试保护者声明的一般问题;是! 绝对测试警卫声明!这些是该方法的行为的重要组成部分,您不想发现有人误解了某个错误,并进行了错误修复并删除了防护措施,或者将其&&改为了||吗?单元测试将确保a)您实际上使警卫正确地掌握了逻辑,并且b)以后在运行单元测试时告诉他们应该出于某种原因,没有人破坏该逻辑而没有抱怨。


0

上面有一些很好的答案,它们提出的要点非常重要。但是似乎遗漏的一个是您拥有一套全面的单元测试,它们看起来像是一个非常详细的代码规范。如果仅由于代码如此简单就忽略了测试验证,很难看到它可能会出错,那么说明的一部分将丢失。如果我进入的团队错过了这些测试,那么我实际上会认为您的服务没有验证其论点。


-5

代码就是代码。测试时,您应该尝试获得100%的覆盖率。如果它不重要,那就不会在那里。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.