保证代码质量和性能。
full lessons here👇:
https://programmerscareer.com/zh-cn/golang-basic-skill/
由AI生成,可能有错误,仅供参考
Topic 1.1: Go 中的测试介绍
测试代码是确保其质量和正确性的基本步骤。测试验证您的代码是否按照预期行为,并使其更不容易出错。如果您在团队中工作,它还可以确保其他人修改不会破坏现有功能。
Go 具有一个内置包叫做 **testing**,它提供了支持自动化测试的功能。Go 的哲学鼓励良好的测试代码,而 testing 包反映了这个哲学,提供了易于使用的特性让您将测试集成到编码实践中。
在 Go 中,每个测试都与一个测试函数相关,这些规则:
- 它必须在以
_test.go
结尾的文件中。 - 测试函数必须以
Test
开头。 - 测试函数只能接受一个参数,即
t *testing.T
。
下面是一个简单的测试示例:
1 | package stringutil |
Topic 1.2: 单元测试
为了确保代码的可靠性,您应该对其最小可测试组件(称为“单元”)进行测试。这类测试被称为“单元测试”。
每个单元测试都是独立的测试用例,验证特定代码部分的功能。在 Go 中,单元测试通常使用 testing 包,正如之前所解释的那样。
当我们创建单元测试时,我们应该遵循以下指南:
- 每个单元测试都应该是独立的,可以单独运行。
- 测试应该简单、清晰和易于理解。
- 测试应该清楚地表明它正在测试什么,以及预期结果。
下面是一个基本示例,使用 strconv.Itoa
函数:
1 | package gorilla |
Topic 1.3: 表驱动测试
测试不同的场景时,经常需要编写多个测试,但它们之间的逻辑几乎相同。这可能会导致大量重复代码。表驱动测试允许您使用单个测试来测试不同输入值。
表驱动测试使用一个单个测试函数,它遍历一个测试用例表。该表是一个数组,包含结构体值,每个结构体表示一个测试用例。
让我们看看一个表驱动测试:
1 | package stringutil |
在这个示例中,我们创建了多个测试用例来测试函数 Reverse
。输入和预期值都定义在 tests
变量中。这些建议在 for 循环中运行,使用 Reverse
函数将输入值调用,并检查输出结果。
如果函数在任何测试用例中失败,它将打印一个错误消息,包括它失败的输入、实际输出和预期输出。
这使得检测哪个测试用例失败了,以及为什么变得更容易。
1.4 Mocking 和接口
在测试复杂应用程序时,我们经常需要处理外部服务、数据库和 API,这些服务通常独立于我们的代码库。在测试中,这些服务可能会有不可预测的行为甚至 unavailable。这个时候,mocking就来了。
mocking 是将软件的一部分(通常是那些具有外部依赖关系的部分)替换为 dummy 实现,以便进行测试。
Go 提供了一种优雅地处理这种情况的方式,即使用 接口。Go 接口使得可以在测试中将实际依赖关系替换为 mock 对象。
下面是一个简单的示例。让我们考虑以下代码:
1 | type Downloader interface { |
在这个示例中,FileDownloader
是我们实际实现的下载文件的方式。函数 processFile
接受一个类型为 Downloader
的参数,这个接口是 FileDownloader
实现的。
现在,如果我们想测试 processFile
函数而不需要访问外部网站,我们就需要创建一个 mock 实现的 Downloader
接口。我们将其称为 MockDownloader
。
1 | type MockDownloader struct { |
在 Test_processFile
测试函数中,我们创建一个 MockDownloader
实例并将其传递给 processFile
函数。这使得函数可以独立于外部依赖关系进行测试。
1.5 测试覆盖率
Go 中的测试覆盖率是衡量代码被测试的程度的指标。它是一个有用的指标,可以帮助您了解您的测试范围。
要计算 Go 的测试覆盖率,我们使用内置的 go test
工具和 -cover
标志。
1 | go test -cover |
这个命令,在您的包目录下运行时,将执行测试并返回以下语句:
1 | PASS |
这个百分比告诉您代码被“覆盖”的程度。
如果您想了解更多关于测试覆盖率的信息,可以使用 -coverprofile
标志来生成一个详细的报告。
1.6 Mocking 和 Benchmark
在上一节中,我们学习了如何使用 mocking 来测试我们的函数。在这个部分,我们将学习如何使用 benchmark 来评估我们的函数性能。
benchmark 是一种评估函数性能的方式。它可以帮助您了解函数执行时间、内存占用等信息。
要使用 benchmark,需要编写一个名为 Benchmark
的函数,该函数接收一个 testing.B
对象作为参数。
例如,我们可以编写一个 benchmark 函数来评估 Reverse
函数的性能:
1 | func BenchmarkReverse(b *testing.B) { |
要运行 benchmark,可以使用 go test
命令和 -bench
标志:
1 | go test -bench=. |
这个命令将执行所有在当前目录下的 benchmark。
通过分析 benchmark 的结果,您可以对函数进行调整以提高其性能。
Topic 1.7: Benchmarking Different Parts of the Code
有时候,你的整个函数可能不是瓶颈,而是某个小部分导致性能下降。在这种情况下,benchmarking整个函数不太有帮助。反之,benchmarking特定的代码部分就很有用了。
然而,在 Go 中,benchmarking特定的代码部分有一点儿 tricky,因为 b.N
循环控制函数执行的次数。如果你想 benchmark某个函数的特定部分,你需要确保那个部分被执行 b.N
次。
下面是一个简单的方法:
1 | package stringutils |
在这个修改后的 benchmark 中,只有字符串的前半部分被反转,这模拟了 benchmarking某个函数的特定部分的过程。
当你运行这个 benchmark 时,可以将其输出与之前的benchmark输出进行比较,从而了解函数中的潜在瓶颈可能在哪里。
请注意,benchmarking本身会增加一些开销,所以在实际程序中最好不要太多地使用 benchmark。它们是你的开发和测试环境中的工具,帮助你编写高效的代码。
Topic 1.8: Analysis and Parsing Benchmark Results
一旦我们已经 benchmarked我们的 Go 代码,我们就需要了解结果。解析benchmark结果涉及理解每个输出数字的含义,以及如何根据代码变化而改变这些数字。
当你在 Go 中运行一个 benchmark 时,通常会获得以下类型的输出:
1 | BenchmarkReverse-4 1000000 1238 ns/op |
下面是这个输出的解释:
BenchmarkReverse-4
:这是测试函数的名称。-4
表示该benchmark使用了 4 个 CPU。1000000
:这是操作被执行的次数。1238 ns/op
:这是操作的成本。在这里,它说我们的 Reverse 函数平均需要 1238 nanoseconds 执行一次操作。
armed with this knowledge,你现在可以比较 benchmark 输出前后,以检查代码变化是否提高了性能或使其更糟。您还可以比较不同函数或函数部分的性能,以找到瓶颈。
Topic 1.9: Overview and Importance of Good Practices
作为软件工程师,我们总是应该努力编写干净、有效和可管理的代码。我们所学到的所有技术,如测试、benchmarking 和分析结果,这些都是更广泛的议程,强调编码良好的实践。
这些实践为什么重要?以下是一些原因:
- 可读性和可维护性:经过测试和 benchmark 的代码可能是干净和可管理的。这使得它更容易被其他人(或你的未来自)阅读和维护。
- 性能:benchmarking 帮助你找到程序中的瓶颈。通过不断优化这些区域,你确保你的代码能够高效地执行。
- 可靠性:通过测试你的代码并修复错误,你增加了程序的可靠性。
- 协作:良好的编码实践,如写出描述性的注释、使用有意义的变量名和逻辑组织代码,这些都使得与其他开发者合作变得更容易。
Topic 1.10: Assessment
现在是时候将你的知识付诸行动了。让我们快速进行一个评估:
- 在自己的语言中,写出如何 benchmark Go 函数的简短描述。
- 什么信息一个典型的 Go 测试输出提供?请给出一个示例,并解释每个部分的含义。
- 为什么遵循良好的编码实践如此重要?有什么这些实践?
English post: https://programmerscareer.com/go-basic-13/
作者:Wesley Wei – Twitter Wesley Wei – Medium
注意:本文为作者原创,转载请注明出处。
评论