GO语言基础笔记(九):工程实践

单元测试(Unit Testing)

        单元测试是指对软件中的最小可测试单元进行检查和验证。在Go语言中,可以使用内置的testing包来进行单元测试。进行单元测试时,您应该关注以下几个要点:

  • 测试代码的独立性:确保每个测试用例都是独立的,不依赖于其他测试。
  • 边界条件的测试:不仅要测试常规条件,也要测试边界和异常情况。
  • 使用表驱动测试:这种方法可以让您用不同的输入重复测试同一功能。

1. 测试代码的独立性

        在单元测试中,每个测试用例应该是独立的,这意味着一个测试用例不应该依赖于其他测试用例的状态或结果。这样做的目的是为了保证测试结果的一致性,确保我们能准确地定位问题。

package mypackageimport "testing"func TestFunctionA(t *testing.T) {// 初始化环境// 执行测试// 断言结果
}func TestFunctionB(t *testing.T) {// 初始化环境// 执行测试// 断言结果
}

2. 边界条件的测试 

        测试不仅应涵盖常规条件,还要包括边界和异常情况。这有助于确保您的代码在各种情况下都能正常工作。

func TestMyFunction_WithBoundaryValue(t *testing.T) {result := MyFunction(边界值)if result != 期望的结果 {t.Errorf("期望得到 %v,但得到了 %v", 期望的结果, result)}
}

3. 使用表驱动测试 

        表驱动测试是一种结构化的测试方法,可以用不同的测试数据多次运行同一个测试逻辑。

func TestMyFunction(t *testing.T) {var tests = []struct {input    输入类型expected 输出类型}{{输入值1, 期望值1},{输入值2, 期望值2},// 更多测试用例}for _, tt := range tests {testname := fmt.Sprintf("%v", tt.input)t.Run(testname, func(t *testing.T) {result := MyFunction(tt.input)if result != tt.expected {t.Errorf("输入 %v,期望得到 %v,实际得到 %v", tt.input, tt.expected, result)}})}
}

        在这个例子中,我们定义了一个结构体数组,其中包含了不同的测试用例。然后我们遍历这些测试用例,对每个用例运行相同的测试逻辑。

        以上就是Go语言中单元测试的一些关键点和示例。希望这些能帮助您更好地理解并实践Go语言的单元测试。继续努力,不断提高!

代码组织(Code Organization)

        代码组织是指如何合理安排代码结构,使其清晰、易于维护。在Go语言中,一般遵循以下原则:

  • 按功能划分包(package):相关的功能应该放在同一个包中。
  • 避免循环依赖:确保包之间的依赖是单向的,不形成循环。
  • 合理利用接口(interface):接口可以帮助解耦,使各个部分更容易独立变化。

1. 按功能划分包(package)

        在Go中,将相关的功能组织到同一个包中是一种常见的做法。每个包应该有一个单一的职责,包中的所有代码都应该服务于这个职责。

        假设您正在开发一个网络应用,您可以将HTTP处理相关的代码放在一个包中,数据库操作相关的代码放在另一个包中。

// http 包处理所有HTTP请求和响应
package httpimport "net/http"func HandleRequest(w http.ResponseWriter, r *http.Request) {// HTTP请求处理逻辑
}// db 包负责数据库交互
package dbimport "database/sql"func QueryDatabase(query string) *sql.Rows {// 数据库查询逻辑
}

2. 避免循环依赖

        循环依赖会导致编译错误,并且使得代码结构复杂化。在Go中,包之间的依赖应该是单向的。

        假设有两个包AB,如果A依赖于B,那么B就不应该依赖于A

// 包A
package Aimport "B"func FunctionA() {B.FunctionB()
}// 包B
package B// 注意:这里没有导入包A,避免循环依赖
func FunctionB() {// 实现逻辑
}

3. 合理利用接口(interface)

        接口是Go语言的核心概念之一。通过定义接口,可以使各个组件之间解耦,更容易进行替换和测试。

package storage// Storer 接口定义了存储行为
type Storer interface {Store(data string) error
}// FileStorer 实现了 Storer 接口,使用文件系统进行存储
type FileStorer struct{}func (f *FileStorer) Store(data string) error {// 文件存储逻辑
}// MemoryStorer 实现了 Storer 接口,使用内存进行存储
type MemoryStorer struct{}func (m *MemoryStorer) Store(data string) error {// 内存存储逻辑
}

        在这个例子中,我们定义了一个Storer接口,它有一个Store方法。然后我们提供了两种实现:FileStorerMemoryStorer。这样,我们可以根据需要替换不同的存储策略,而不影响使用这些策略的代码。 

模块化(Modularization)

        模块化是指将系统分解为多个模块,每个模块实现特定的功能。在Go中,模块化可以通过创建不同的包(package)来实现。您应该注意:

  • 明确每个包的职责。
  • 保持模块间的低耦合性。
  • 通过导出和非导出标识符控制访问权限。

1. 明确每个包的职责

        在Go中,一个包应该代表一个单一的、明确的功能模块。这意味着包内的所有代码都应该服务于一个共同的目标。

        假设您正在开发一个电商应用,您可能需要一个处理订单的包和一个处理用户账户的包。

// orders 包处理订单相关的逻辑
package orders// ProcessOrder 处理订单
func ProcessOrder(orderID int) {// 订单处理逻辑
}// users 包处理用户账户相关的逻辑
package users// CreateUser 创建新用户
func CreateUser(username string) {// 用户创建逻辑
}

2. 保持模块间的低耦合性

        低耦合性是指各个模块之间相互独立,减少了模块间的依赖。这样的设计使得每个模块都可以独立地进行更改和维护。

        在电商应用中,订单处理模块不应直接依赖于用户账户模块的内部实现。

package ordersimport "users"// ProcessOrder 使用了 users 包的接口,而不是具体实现
func ProcessOrder(orderID int, userID int) {user := users.GetUser(userID)// 使用 user 进行订单处理
}

3. 通过导出和非导出标识符控制访问权限

在Go中,通过大写字母开头的标识符实现导出(公开),这意味着其他包可以访问这些标识符。小写字母开头的标识符是非导出(私有)的,只能在同一包内访问。

示例说明:

合理地使用导出和非导出标识符,可以控制包的内部实现细节,只向外界暴露必要的接口。

package users// CreateUser 是导出的,可以被其他包调用
func CreateUser(username string) {// 实现细节
}// hashPassword 是非导出的,只能在 users 包内部使用
func hashPassword(password string) string {// 密码哈希逻辑
}

        以上示例展示了如何在Go中实现模块化。明确的职责分配、低耦合性设计和合理的访问权限控制,这些都是构建模块化系统的关键。遵循这些原则,您将能够创建出更加健壯、易于维护和扩展的Go应用。

依赖管理(Dependency Management)

        Go语言自1.11版本起引入了模块支持(Go Modules),这是Go语言的官方依赖管理系统。使用Go Modules可以轻松地管理项目的依赖。关键步骤包括:

  • 使用go mod init创建新模块。
  • 使用go get获取依赖。
  • 使用go.modgo.sum文件管理依赖。

使用 go mod init 创建新模块

        当您开始一个新项目或将现有项目转移到 Go Modules 管理时,首先需要初始化模块。

        在项目根目录下运行以下命令:

go mod init github.com/用户名/仓库名

使用 go get 获取依赖

  go get 命令用于下载项目依赖的模块到本地。

        假设您想要获取 github.com/gin-gonic/gin 这个 HTTP web 框架,您可以运行:

go get github.com/gin-gonic/gin

        这个命令会下载 gin 并更新 go.modgo.sum 文件。

使用 go.modgo.sum 文件管理依赖

   go.mod 文件包含了项目所需的依赖信息,而 go.sum 文件包含了依赖的特定版本的校验和(checksums),用于确保依赖的完整性和一致性。

  go.mod 文件示例:

module github.com/用户名/仓库名go 1.16require (github.com/gin-gonic/gin v1.6.3// 其他依赖
)

   go.sum 文件示例

github.com/gin-gonic/gin v1.6.3 h1:... // 校验和
github.com/gin-gonic/gin v1.6.3/go.mod h1:... // 校验和

其他常用命令

  • go list -m all:列出当前模块的所有依赖。
  • go build:构建项目时,会自动下载依赖到本地。
  • go mod tidy:清理未使用的依赖。

这个Go语言系列 就先到这吧。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/589768.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

P1163 银行贷款

P1163 银行贷款 P1163 银行贷款 题意 1.从银行贷款后,在一段时间内每月偿还固定的分期付款。这个问题要求计算出贷款者向银行支付的利率。假设利率按月累计。 2.三个用空格隔开的正整数。第一个整数表示贷款的原值n,第二个整数表示每月支付的分期付款金…

解决idea文件properties中文乱码问题

解决idea文件properties中文乱码问题_idea properties文件中文乱码-CSDN博客

[react]脚手架create-react-app/vite与reac项目

[react]脚手架create-react-app/vite与reac项目 环境问题描述create-react-app 脚手架根据脚手架修改项目结构安装脚手架注入配置文件-config文件夹package.json文件变更删除 serviceWorker.js新增reportWebVitals.js文件更新index.js文件 脚手架creat-react-app 缺点 vite 脚手…

基于Springboot实现天天生鲜销售电商平台

SSM毕设分享 基于Springboot实现天天生鲜销售电商平台 1 项目简介 Hi,各位同学好,这里是郑师兄! 今天向大家分享一个毕业设计项目作品【】 师兄根据实现的难度和等级对项目进行评分(最低0分,满分5分) 难度系数:3分 …

中文字符占用字节即相关原理(实现中文(中英混合)字符串的反转)

如有不对欢迎指正。 目录 一.ASCLL字符和中文字符 1.使用无符号数表示的原因(对于中文字符): 2.但是并不是所有情况都是用无符号数(以下目前只是猜测,如有问题欢迎指正) : 1. 什么时候使用无符号数表示: 2. 不需要使用的情况: …

vue源码(一)

搭建环境 获取地址:GitHub - vuejs/vue: This is the repo for Vue 2. For Vue 3, go to https://github.com/vuejs/core 安装依赖:npm i 安装 rollup: npm i -g -rollup 修改dev脚本:添加 --sourcemap "dev": &q…

【最优化方法】精确一维搜索方法

文章目录 0.618 法示例 Newton 法示例 0.618 法 0.618 法是一种一维线性搜索的算法,也称为黄金分割法(Golden Section Method)。它是一种迭代方法,用于确定目标函数在一个区间上的最小值。 以下是 0.618 法的基本步骤&#xff1…

Access数据库C#读写验证

1、数据库简介 Access数据库是一个相当古老的文件型数据库,主打一个简单方便,没有复杂的安装过程,没有庞大的后端管理,整个数据库就是一个文件。可以像普通文件一样复制和修改,可以同时读写。 在小型系统中&#xff0c…

子组件如果向父组件传递数据是怎么做到

在 Vue.js 中&#xff0c;子组件向父组件传递数据可以通过自定义事件来实现。子组件可以通过 $emit 方法触发一个自定义事件&#xff0c;并且可以传递数据给父组件。 下面是一个示例&#xff0c;演示了子组件向父组件传递数据的过程&#xff1a; <!-- ParentComponent.v…

Xgboost分类模型的完整示例

往期精彩推荐 数据科学知识库机器学习算法应用场景与评价指标机器学习算法—分类机器学习算法—回归PySpark大数据处理详细教程 定义问题 UCI的蘑菇数据集的主要目的是为了分类任务&#xff0c;特别是区分蘑菇是可食用还是有毒。这个数据集包含了蘑菇的各种特征&#xff0c;如…

【读书笔记】网空态势感知理论与模型(四)

一个网空态势感知整合框架 1. 引言 网空态势感知过程可以看作包含态势观察、态势理解和态势预测3个阶段。 态势观察&#xff1a;提供环境中相关元素的状态、属性和动态信息。 态势理解&#xff1a;包括人们如何组合、解释、存储和留存信息。 态势预测&#xff1a;对环境&a…

系统学习Python——装饰器:函数装饰器-[装饰器状态保持方案:外层作用域和非局部变量]

分类目录&#xff1a;《系统学习Python》总目录 我们在某些情况下可能想要共享全局状态。如果我们真的想要每个函数都有自己的计数器&#xff0c;要么像前面的文章那样使用类&#xff0c;要么使用Python3.X中的闭包函数&#xff08;工厂函数&#xff09;和nonlocal语句。由于这…

浪潮软件开发校招面试一面凉经

本文介绍2024届秋招中&#xff0c;浪潮通信信息系统有限公司的软件开发工程师岗位一面的面试基本情况、提问问题等。 10月投递了浪潮通信信息系统有限公司的软件开发工程师岗位&#xff0c;并不清楚所在的部门。目前完成了一面&#xff0c;在这里记录一下一面经历。 首先&#…

听GPT 讲Rust源代码--library/alloc

File: rust/library/alloc/benches/slice.rs 在Rust源代码中&#xff0c;rust/library/alloc/benches/slice.rs文件的作用是对&[T]类型&#xff08;切片类型&#xff09;进行性能基准测试。该文件包含了对切片类型的一系列操作的基准测试&#xff0c;例如切片迭代、切片排序…

【Python百宝箱】音律编织:Python语音合成库的技术交响曲

未来声音的奇妙之旅&#xff1a;深度学习与云端语音服务的交汇 前言 在当今数字化时代&#xff0c;语音合成技术在各个领域的应用日益广泛&#xff0c;从辅助技术到娱乐媒体&#xff0c;都展现出巨大的潜力。本文将带您深入了解语音合成的世界&#xff0c;从简单易用的库如py…

0101包冲突导致安装docker失败-docker-云原生

文章目录 1 前言2 报错3 解决结语 1 前言 最近在学习k8s&#xff0c;前置条件就是要安装指定版本的docker&#xff0c;命令如下 yum install -y docker-ce-20.10.7 docker-ce-cli-20.10.7 containerd.io-1.4.62 报错 file /usr/libexec/docker/cli-plugins/docker-buildx fr…

微信多商户商城小程序/公众号/h5/app/社区团购/外卖点餐/商家入驻/在线客服/知识付费/商品采集

多个源码二开合一!!包含:多商户商城/社区团购/外卖点餐/在线客服/知识付费/投票 。。。等等!!! 前台可自定义装修!!装修成为如下程序 1、小程序,公众号,h5,app多端合一 2、用户论坛 积分签到 3、知识付费、题库管理、课程设置 4、同城配送,配送员设置 5、餐饮…

用 print 太慢了!强烈推荐这款Python Debug工具~

作为程序员&#xff0c;我们都深知调试&#xff08;Debug&#xff09;在编程过程中的重要性。 然而&#xff0c;使用传统的"print"语句进行调试可能效率较低&#xff0c;今天&#xff0c;笔者将推荐一款独具一格的Python调试工具——Reloadium。 Reloadium为IDE添加…

sparkstreamnig实时处理入门

1.2 SparkStreaming实时处理入门 1.2.1 工程创建 导入maven依赖 <dependency><groupId>org.apache.spark</groupId><artifactId>spark-streaming_2.12</artifactId><version>3.1.2</version> </dependency> <dependency…

C++初阶——基础知识(内联函数)

目录 1.内联函数 内联函数的示例代码 1.内联函数 是一种 C 中的函数定义方式&#xff0c;它告诉编译器在每个调用点上插入函数体的副本&#xff0c;而不是像普通函数那样在调用时跳转到函数体所在的地址执行。这样可以减少函数调用的开销&#xff0c;提高程序的执行效率。 …