超简短摘要:当出现错误时退出程序可能是个好主意。使用 gobail 会让您的生活更轻松。
当你的 Go 代码出现错误时,你通常会看到类似这样的内容:
err := myFunc() if err != nil { return fmt.Errorf("doing my thing: %w", err) }
您会在这个示例中注意到一些事情:
那么接下来会发生什么?好吧,这一切又发生了。您检查错误值,描述它,然后将其传回。然后一切又重新开始。
我们为什么要这样做?为什么要这么努力?
这一切都取决于您正在编写的软件。当你遇到错误时,你必须做出决定。这个错误会发生什么?
如果您正在编写一个响应请求的 HTTP API,那么最终您将获得某种 HTTP 处理程序,并将该错误转换为某种响应 - 也许是一个带有礼貌提醒格式的小 400正确请求或可能返回 500 以及有关应用程序运行状况的令人担忧的消息。或者,如果您正在编写某种 CLI 工具,那么您可能会决定错误最终会一直传递回您的主函数。对于任何类型的程序,您可能会认为已经足够了 - 该程序应该结束,因为您无法做任何其他事情。
让我们看看最后一个选项。什么时候退出程序合适?我能想到的几个原因:
a.没有别的办法了,错误太严重了,一切都必须立即停止
b.终止程序没有任何后果(不需要清理,没有状态可以响应)
c.尽早停止是可取的,也许你有一个监视器可以干净地重新启动该过程
无论什么原因,你都需要考虑如何干净利落地退出。现在您可能尝试的第一件事是:
err := myFunc() if err != nil { fmt.Printf("doing my thing: %v", err) os.Exit(1) }
它看起来与我们原来的错误处理代码非常相似,但有一些重要的区别。第一个是显而易见的——那里有一个很棒的stop-right-g*****n-now声明。你的程序不会继续下去。第二点也许更重要。调用此示例的代码不必担心处理任何错误。没有需要测试的其他代码路径 - 我们可以相信调用代码没有要测试的 if 块,因为没有返回任何需要我们检查的内容。
所以当我建议你应该相信你的退出代码会起作用时,我可能有点热情。您可能应该检查新程序停止的原因是否正确。
这感觉应该很容易。这里有一些需要考虑的事情 - 在最简单的情况下,您只需运行程序并触发错误条件。例如,让 CLI 工具打开一个不存在的文件。对于一些简单的情况,您可以手动执行此操作。当测试数量增加时,您可能需要某种自动化来帮助您。
快速旁注 - 这可能是另一篇博客文章的主题,但我目前最喜欢的测试 CLI 工具的方法是使用 godog 来编写测试。它可能有点复杂,但我发现它非常强大。以下是我如何使用layli和wait-for来处理它的一些很好的例子。
这种方法会让您走得很远,但有时可能很难创造条件来正确执行您想要确信的所有代码路径。
好的,现在我们将使用 Go 语言的一些功能。我们实际上不必调用 os.Exit - 我们可以调用看起来像它的东西。所以看看这个:
err := myFunc() if err != nil { return fmt.Errorf("doing my thing: %w", err) }
那么我们如何利用这一点进行测试呢?由于函数现在已转换为变量 (customExit),因此我们可以用我们想要执行的其他操作替换该值。就像这样...
err := myFunc() if err != nil { fmt.Printf("doing my thing: %v", err) os.Exit(1) }
这是一种对单元测试更加友好的方法。您可以检查使用的退出代码是否正确 - 并且您实际上调用了退出函数。
从表面上看,这看起来不错,但有一个大问题 - 如果您的测试通过,那么您的程序将继续并在您期望函数退出时执行该函数的其余部分。即使测试设置意味着其余的执行无效并导致测试出现问题(例如引起恐慌),它仍将继续。
嗯,这听起来有点极端!
我觉得我应该解释一下......通常在“管理良好”的公司中,您需要确保每一行代码都已被证明是有效的,然后才能放在客户面前。使用上述技术,您可能无法生成正确的覆盖率指标来证明您的能力良好。即使推理起来微不足道。
上面的所有示例都假设当我们收到错误时,我们必须检查它以决定做什么(报复性退出)。如果我们能够退出而无需检查是否存在错误,那不是很好吗?
让我们看看我们能做什么。
err := myFunc() if err != nil { return fmt.Errorf("doing my thing: %w", err) }
看一下上面的例子。功能是相同的,但 myFunc 的实现现在更加简单 - 没有条件。我们可以在自己的测试中检查 checkExit 函数的实现,这意味着 myFunc() 中的任何新内容都可以更容易地验证。
创建了一个新的库 gobail,它可以让您确信如果出现错误,它将得到处理,而无需增加您自己的代码的复杂性。看起来像这样:
err := myFunc() if err != nil { fmt.Printf("doing my thing: %v", err) os.Exit(1) }
这个库已经过全面测试,并具有覆盖率指标,以证明这一点。您可以安全地使用它,而不必担心错误会被跳过。它还将处理具有 2 个返回值的函数,如下所示:
type ExitFunc func(code int) var customExit ExitFunc = os.Exit func myFunc() { err := someOtherFunc() if err != nil { fmt.Printf("doing my thing: %v", err) customExit(1) } }
另请注意,您包含了导致所有问题的错误。
也可以发生恐慌而不是退出,在调用恐慌时打印堆栈跟踪和程序中的其他上下文信息。请查看文档以了解更多详细信息。
当您使用 gobail 编写软件时,您会注意到在与外部库交互时大多数情况下都必须使用它。这具有您通常需要编写来处理所有错误情况的附加代码,可以将其包装在对 Return 或 Return2 的调用中,并假设我们将在必要时退出。
有时需要退出程序而不是详细处理错误。 gobail 库已创建并经过验证,因此您不必担心证明这一点的细节。
如果您发现可以进行的改进或只是有建议,请在存储库上提出 PR 或问题,开发人员会尽快处理!
以上是为了胜利早早退出!的详细内容。更多信息请关注PHP中文网其他相关文章!