可以说我有一个事件侦听器,只要有人更改了位置,就会调用该侦听器。这是一些Java代码。

public class LocationManager() {
//..
..
..//

public void updateLocationFromGPS(String userID, String locationName) {
    if(userID!=null && locationName!=null) {
        for(LocationChangeCallback callback : locationCallbacks)
        {
            callback.onLocationChange(userID, locationName, EventSource.GPS);
        }
    }

    }
}


回调函数...

public class Callback implements LocationChangeCallback {

public void onLocationChange(String userID, String locationName, EventSource eventSource) {
//DO SOME WORK


}

}


事件发送到的位置并且收到的回调位于两个不同的组件中,因此我将需要对它们进行单元测试。现在,知道在调用回调方法之前,我会对值进行空检查,并且仅在值不为空时才发送事件。

因此,回调中接收到的值将永远不会为空。最好的方法是在回调中添加测试以检查空值(并更改我的代码以相应地通过)吗?如果是这样,为什么?

评论

您在标题中询问了单元测试,并在问题正文中询问了编码实践。实际上,这是两个问题。这应该反映在标题中,并从您的问题正文中清楚看出。还是您实际上是想只问一个问题-如果是,请问哪个?

@prockel只是有关单元测试的一个问题,尽管我需要编辑代码以通过测试。但是,是的,我应该说得更清楚,谢谢!

#1 楼

是的。因为单元测试不仅适用于当前版本的代码。它们特别适用于所有将来的版本,如果有人更改了代码并且不知道他/她做了什么,则测试可能会失败并告诉他他做错了什么。所以你不能说这将永远不会发生。

单元测试不仅会测试代码,它们也是文档。

评论


我没有想到如果将来有人更改代码会发生什么。万分感谢。

– Stu Whyte
2014年6月24日14:15



实际上,它们最终只是文档,因为它们恰好是工作示例。但是,我会因为您有单元测试而在文档上有所懈怠。单元测试应该测试记录的要求。该文档可以告诉您您的测试是否在测试正确的东西。至于答案本身,就在现场。测试永远不会发生的事情是最简单的测试-如果您破坏了某些不变性(例如“ this不能为空”),则显然存在一个错误(被测试捕获)。

–corsiKa♦
14年6月24日在20:34

抱歉-我想您有点混混了。单元测试应根据预期结果测试输入。如果回调定义为“空输入行为无关”,则要测试的单元测试为“如果为null,则为assert(true)”。所以您根本不需要它... *查看我的答案

– Falco
2014年6月25日上午10:51

如果最终从代码中的其他任何地方调用此方法,则在调用该方法之前,必须对空值进行相同的精确检查。我建议实际上将对null值的检查移到方法本身中,这样就不需要很容易被遗漏的重复代码。然后,您可以让单元测试验证传递给该方法的两个值是否为null,以表明该方法将跳过任何操作。

–山姆·伍兹(Sam Woods)
2014年6月25日15:39

#2 楼

这里有两个问题。

一个,是否应该对意外输入进行单元测试?是。任何可编译的输入都是有效的单元测试,应定期测试空值,边界条件和其他特殊值。这是一种公共方法,您不知道将来可能会在哪里调用它,或者别人可能会做什么。

第二,如果您的单元测试发现了一些意外的结果,那么您应该采取防御措施进行编码避免意外的结果?有时。如果您的函数可以从给定的输入中产生有意义的结果,则应对其进行修改以这样做。如果您的函数无法产生有意义的结果(例如,如果在文件不存在时要求您打开文件进行读取),则应更新单元测试以期望出现错误或异常。无论哪种情况,您都应该以通过单元测试结束。

编辑:此外,就个人而言,单元测试的一大好处是,您可以按需生成边缘和特殊情况,即使它们是在现实世界中很难找到或看起来不可能。关于系统的轶事可以正常运行多年,直到突然之间不是因为有人在数据库的某个角落改变了某个值。

#3 楼

如果它是onLocationChange的“已定义接口”的一部分,不能用null调用它,那么就没有太多的方法可以测试其对null的行为。您不在乎它对null的行为是什么;如果曾经用null调用过它,则该错误就在调用方中。

由此,我们还可以得出结论,您应该绝对对所有调用方(包括updateLocationFromGPS)进行单元测试,从而确保他们从不将null传递给onLocationChange。因此,宣告“ null无效,如果得到它们,这是调用者的错”通常会增加测试负载,而不是减少测试负载。

另一方面,如果存在一些明确定义的行为当给定null时,则应该对null的行为进行单元测试。即使您目前从未在代码库中传递空值,即使定义良好的行为只是引发某些特定异常也是如此。

#4 楼

阅读答案后,我认为这是一个重要的区别。 “单元测试”和“检查代码中的参数”是两件事。

这是检查参数。我的方法在执行无效操作之前先检查是否存在null值。

它记录了您在方法中所作的假设,强制执行了这些假设并很快失败了。不仅仅是空值,这是一件好事。同样,如果它们的区别足够大,可以分别进行测试,那么它们的区别足够大,可以分别重用。

所以问题正文(应该检查参数)的答案是肯定的

public void Foo(string myString){ 
    Guard.IsNotNull(myString);
    return foo.ToUpper();
}


这是单元测试

public static void Foo(string myString){ 
    return foo.ToUpper();
}

[TestMethod]
[ExpectedException(typeof(NullReferenceException),"It went bang")]
public void TestWithNulls()
{
   SomeClass.Foo(null);
}


这并不是非常有用。不要在这里做。除非您觉得方法成功是功能的关键部分。

#5 楼

好的-那么单元测试的目的是什么?根据文档/要求对方法的行为进行必要的测试/验证。

因此,如果方法记录为“始终返回1”,则您将设计单元测试以验证是否输入是,函数总是返回1。

您的回调似乎定义为“执行XYZ操作。如果Input为NULL,则行为异常”。如果这是您的要求和Method的文档,不必为输入为NULL编写单元测试,因为根据定义,您的方法所做的任何事情都是正确的行为!

因为您将在单元测试中进行什么测试?输入= NULL->预期结果= ???如果规范中没有定义的行为,您会断言什么?

评论


他可以在被测方法中抛出一个异常,并断言将抛出一个异常。

– Twaldigas
2014年6月25日在11:31



或者,如果被测方法更改了数据库或类似数据库中的某些内容,则单元测试可以检查数据库是否不变。

– Twaldigas
14年6月25日在11:37