我在测试中没有发现bug,所以系统没有bug,对吧?
不幸的是,大规模的软件太复杂,无论多少测试都无法做到没有bug。你无法对用户使用应用程序的所有不同方式进行测试。因此,理解应用程序中错误和异常的区别是非常重要的,同时要了解处理它们的正确方法,以便你可以采取主动的方式为开发团队和终端用户提供健康的应用程序。
测试的局限性
即使使用最彻底的测试过程,仍然只是在测试特定的情况,并且包含了自己的想法在其中。
想象一下,突然有成千上万的用户以你没有想到的方式使用你的应用程序——他们几乎肯定会碰到你在测试时没有碰到的东西。
如何正确处理应用程序中的错误
简单地说,bug可以导致错误和异常。错误和异常是有不同含义的术语。
主要的问题应该是如何更好地处理这些错误和异常,使它们不会产生负面后果。
首先,让我们看看一些定义,以及为什么这些区别很重要。
错误和异常——有什么区别?
一些编程语言有它们自己的错误和异常定义,但是我想定义它们的区别。
错误
无法优雅地恢复/继续的编程错误,通常需要开发人员介入并修改代码来进行修复。有时可以将错误转换为异常,以便在代码中处理它们。
通过简单检查可以避免错误,如果简单检查不能满足要求,错误还可以转化为异常,以便应用程序能够优雅地处理这种情况。
异常
利用特定语言的语义,并在发生异常时表示。异常可以被捕获和抛出,以便代码可以恢复和处理这种情况,而不进入错误状态。
可以抛出和捕获异常,以便应用程序能够正常恢复或继续。还可以记录未处理的异常(即错误),以便开发人员查看它们以修复底层错误。
示例# 1
用户错误——用户输入错误数据,也不需要异常处理,但仍可能导致错误/不可恢复状态。代码应该进行简单的检查,以防止发生这种情况。应该使用前端和后端验证,对于本例,只抛出一个异常作为“最后的防御”。
示例# 2
文件无法打开并抛出FileLoadException或FileNotFoundException。这是一种特殊情况,不应该破坏应用程序。应用程序应该能够处理这种情况,因为这种情况可能由于许多原因而发生,因此,你必须预期到这种情况。
该出错的还是会出错……至少一次
“所以……如果我捕捉到每个异常,我的代码就不会出错了,对吧?”
正如我前面提到的,并非所有错误都会导致异常。这个结论的主要问题是你不知道哪里出了问题。你的代码可能存在许多问题,由于捕获异常而对其不做任何处理,你会丢失这些信息。
不要只是捕捉每个异常,然后继续,就像什么都没有发生过一样。捕获块的目的是在一定情况下进行处理。
不要做什么——把他们捕获起来。
如何编写应用程序来恢复自己
抛出和捕获异常是让应用程序自行恢复并防止它运行到错误状态的好方法。
如果你知道可能会抛出哪一种类型的异常,最好在catch块中显式地显示,因为每种不同类型的异常都将意味着代码由于不同的原因而停止。
异常类型要具体,这样就可以向用户提供反馈,并在确切知道失败的原因后更优雅地处理其他情况。
为什么指定要捕捉哪种类型的异常很重要?
某些异常可能破坏数据或以意外的方式运行,这取决于你的程序如何继续运行。这将导致应用程序出现错误。
如果你确切地知道发生了哪种异常,那么你应该知道要按照哪些步骤进行恢复。或者,如果无法恢复,你应该知道如何优雅地处理这种情况。
那么,它能恢复吗?在很多情况下,异常具有足够的信息来知道发生了什么错误,并且在catch块中,你有时可以从错误状态中恢复。可以通过修复一些数据、重新获取数据或甚至要求用户再试一次。
你可以捕获异常,但有时应用程序仍然无法继续运行,因为它所依赖的数据已经以不可恢复的方式被损坏,或者它期望数据采用不同的格式。
示例
数组上的OutOfRangeException异常?一个程序如何才能恢复?这是一个将错误转化为异常的例子。你的应用程序期望数据以某种方式出现,但这并没有发生。虽然恢复并不总是可能,但现在可能不进入错误状态并优雅地处理这种情况。如果将此记录下来,开发人员可以通过在访问数组之前添加一些简单的检查或更改访问它的方式来修复此问题。
如何处理未处理的异常
有一些你意想不到的异常,通常表示代码中的错误。你可以记录那些没有被代码捕获的未处理的异常,因为大多数语言都提供了这样做的方法。任何未处理的异常都表示错误。你的代码没有预料到这一点,因此无法正常恢复或处理这种情况。
将它们记录下来是一个好主意,这样就可以修复。这样,错误就不会经常抛出异常。如果它们真的发生了,你想要了解它们,这样你就可以捕获它们并处理它们。
错误日志
错误日志可以帮助捕获这些错误。有一个可以查看这些日志错误/异常的地方是调试的关键,也是确定何时修复什么问题的优先级。
此外,你也不希望依赖于屏幕截图和已经沮丧的用户提供的更多信息。错误日志记录还可以使你的团队在出现错误时能够积极主动地联系受影响的用户。这是为了让他们知道你正在解决问题,这不仅会促进你的客户关系,而且你还可以在其他用户遇到错误之前修复错误。
示例
代码中产生多个不正确的账单费用的错误通常比无法显示特定详细信息页面的错误更重要,即使详细信息页面错误发生得更频繁。
最终,你希望应用程序遇到的异常尽可能少,但是当它遇到异常时,你希望了解它。只有1%的用户报告错误,所以还有很多错误仍然存在。
解决部分问题
编写一些代码将异常和堆栈保存到文件中,或者通过电子邮件发送,以便在发生错误时得到通知,这可能是部分解决方案。
示例
一个用户会遇到上千个异常。100个用户也遇到了较少发生的错误。哪个更重要?在不知道错误细节的情况下,影响更多用户的错误更重要。
使用异常的堆栈应该有助于定位错误可能出现的位置,并且你应该能够重新生成它或阅读代码以理解那里出了错。
有时这还不够,问题需要进一步调查。如果发生这种情况,在记录异常之前向异常添加更多信息,包括上下文特定细节(例如帐户id或特定对象状态),这些信息将允许你在本地重新生成错误。
是时候修正错误了
现在,你应该已经捕获了所有的错误和异常,并记录未处理的错误……现在该怎么办呢?
根据应用程序的规模,错误通知中的噪音是一个问题。你可以使用电子邮件过滤/grep做一些聪明的事情,它可以分组错误并分离到不同的文件夹/文件中。这可能有所帮助,但只是对噪音问题的部分解决方案。
几年前,我个人也这么做过,但很快意识到这只是一个部分的解决方案,原因有很多。问题是,我仍然不知道哪些错误对用户的影响最大。我关注的是那些抛出的错误,而不是那些对应用程序/用户体验最有害的错误——正因为如此,我从未真正清楚地知道哪里出了问题。
我无法看到发生了什么,但必须运行手动查询才能弄清楚,这非常耗时。
对于大型软件,总是会抛出错误和异常。正确地处理错误将有助于将你定义为一个软件团队,并围绕异常和错误创建更好的过程。
好的应用程序包含可以在可能的情况下从异常中恢复的代码。处理和记录异常对软件的运行状况非常重要!
欢迎关注我的公众号,如果你有喜欢的外文技术文章,可以通过公众号留言推荐给我。
原文链接:https://dzone.com/articles/how-to-handle-errors-and-exceptions-in-large-scale