Go -- 测试 and 项目实战

没有后端基础,学起来真是费劲,所以打算速刷一下,代码跟着敲一遍,有个印象,大项目肯定也做不了了,先把该学的学了,有空就跟点单体项目,还有该看的书....

目录

🍌单元测试

🌼assert

🌼覆盖率

🦊tips

🦊依赖

🦊文件处理

🍌Mock测试

🍌基准测试

🍉项目实战

🦂ER图

🦂分层结构

🦂组件工具

💪Repository

💪Service

💪Controller

💪Router

💤运行


🍌单元测试

规则

(1)测试文件以 _test.go 结尾

源代码与测试代码👇

(2)测试函数命名规范:func TestXxx(*testing.T)     Test紧挨着第一个字母大写👇

(3)初始化逻辑放到TestMain()里

🌼assert

导入开源网站的assert包,来进行判等,不等等测试操作👇

单元测试的样例👇

judge.go

package judge

func JudgePassLine(score int16) bool {
	if score >= 60 {
		return true
	}
	return false
}

judge_test.go

package judge

import (
	"github.com/stretchr/testify/assert"
	"testing"
)

func TestJudgePassLineTrue(t *testing.T) {
	isPass := JudgePassLine(70)
	assert.Equal(t, true, isPass)
}

Run一下👇

👇

👆解释

testing.T 是 Go 语言测试框架中的一个结构体类型,它提供了一组方法和属性用于管理和报告测试的结果。当你编写和运行测试函数时,测试框架会自动创建一个 testing.T 类型的实例,并将其传递给测试函数

🌼覆盖率

当我们写了单元测试后👇

覆盖率越高,代码的质量越有保证,那如何查看覆盖率呢👇

PS:发现个Goland小技巧,ctrl + z可以返回上一步代码,误删了也不要紧

要查看覆盖率,首先要切到对应目录下,视频中是👇

但我只会先cd到对应目录,结果一样👇

覆盖率意味着👇

judge.go,函数中一共3行有效代码,但是judge_test.go传入的70,只会运行前2行,所以覆盖率是 2 / 3

当我们需要100%覆盖率,只需要增加一个测试分支👇

func TestJudgePassLineFail(t *testing.T) {
	isPass := JudgePassLine(50)
	assert.Equal(t, true, isPass)
}

👆通过不断地,对各个分支的测试,保证了测试的完备性, 减少了BUG的产生

🦊tips

🦊依赖

DB:数据库database

Cache:Redis类似的组件

File:本地文件

👆三项属于项目中的强依赖

在单元测试中,一般有2个目标:(1)幂等  (2)稳定

(1)幂等:多次重复一个case的测试,结果一样

(2)稳定:单元测试之间是相互隔离的,即任何时间 / 函数下,都能够运行

而需要保证幂等 / 稳定,需要Mock机制,下面先讲文件处理

🦊文件处理

log

line11
line22
line33
line44
line55

ProcessFirstLine.go

package firstLine

import (
	"bufio"
	"os"
	"strings"
)

// ReadFirstLine 从文件中读取第一行内容
func ReadFirstLine() string {
	open, err := os.Open("log") // 打开名为 "log" 的文件
	defer open.Close()          // 延迟关闭文件, 避免资源泄露
	if err != nil {
		return "" // 发生错误时返回空字符串
	}
	scanner := bufio.NewScanner(open) // 创建一个扫描器
	for scanner.Scan() {              // 循环遍历文件的每一行
		return scanner.Text() // 返回第一行内容
	}
	return "" // 文件为空时返回空字符串
}

// ProcessFirstLine 处理第一行内容,将 "11" 替换为 "00"
func ProcessFirstLine() string {
	line := ReadFirstLine()                          // 调用 ReadFirstLine 函数获取第一行内容
	destLine := strings.ReplaceAll(line, "11", "00") // 将 "11" 替换为 "00"
	return destLine                                  // 返回替换后的结果
}

ProcessFirstLine_test.go

package firstLine

import (
	"github.com/stretchr/testify/assert"
	"testing"
)

// TestProcessFirstLine 是对 ProcessFirstLine 函数的单元测试
func TestProcessFirstLine(t *testing.T) {
	firstLine := ProcessFirstLine()      // 调用 ProcessFirstLine 函数
	assert.Equal(t, "line00", firstLine) // 使用断言验证结果是否符合预期
}

equal还是报错,但是点击这里还是可以PASS👇

解释

(1)bufio:高效读取数据的包

  • bufio.NewReader():创建一个新的Reader对象,用于读取数据。它接收一个io.Reader类型的参数,并使用默认大小的缓冲区
  • bufio.NewScanner():创建一个新的Scanner对象,用于对输入流进行扫描。Scanner可用于逐行读取文本等
  • Reader类型:它提供了各种方法来从输入流中读取数据,如ReadString()用于读取字符串,ReadBytes()用于读取字节切片等
  • Scanner类型:它提供了各种方法来解析输入流中的数据,如Scan()用于逐行读取文本,ScanBytes()用于逐个字节读取等
  • Scanner.Text():返回当前扫描的文本内容
  • Scanner.Scan():将扫描器移动到下一行,并返回是否还有更多行可供扫描

(2)os是一个提供与操作系统相关功能的包

  • os.Args:一个字符串切片,包含命令行参数
  • os.Exit(code int):终止当前程序的执行,并返回给定的错误码
  • os.Getwd():返回当前的工作目录的路径名
  • os.Chdir(dir string):将当前的工作目录更改为指定的目录
  • os.Mkdir(name string, perm FileMode) error:创建一个新目录
  • os.Open(name string) (*File, error):打开一个文件用于读取
  • os.Create(name string) (*File, error):创建一个文件用于写入
  • os.Remove(name string) error:删除指定的文件或目录
  • os.Rename(oldname, newname string) error:重命名(移动)文件或目录
  • os.Stdoutos.Stdinos.Stderr:标准输出、标准输入和标准错误输出的文件对象

但是,如果源文件被人篡改,那么测试文件,在特定场景下,也就无法运行,那如何Mock呢?👇

🍌Mock测试

开源Mock测试包 -- bouk/monkey: Monkey patching in Go (github.com)

打桩,即用函数A替换函数B👇

不得不吐槽一下,内部课贴的代码只贴部分,默认剩下部分你都会了,没学过也不能无中生有吧...真的觉得,每一点代码都力求自己得到对应输出,有点浪费时间了,先速通吧...

🍌基准测试

看了一遍...

🍉项目实战

描述

用例

👆用户浏览页面,主要展示2方面内容,一是Topic话题,一是PostList回帖的列表

🦂ER图

Entity Relationship Diagram

ER图由以下三个主要组成部分构成:实体,属性,关系。

🦂分层结构

数据层:数据Model,外部数据的增删改查

逻辑层:业务Entity

视图层:视图view,处理和外部的交互逻辑

🦂组件工具

(1)Gin高性能 go web 框架

gin-gonic/gin: Gin is a HTTP web framework written in Go (Golang). It features a Martini-like API with much better performance -- up to 40 times faster. If you need smashing performance, get yourself some Gin. (github.com)

(2)Go mod

go mod init

go get gopkg.in/gin-gonic/gin.v1@v1.3.0 --> 执行后,go.mod就有gin的依赖

💪Repository

index

两个索引👇

初始化话题数据索引👇

package main //入口

import (
	"bufio"         //缓冲读取
	"encoding/json" //处理JSON数据
	"os"            //操作系统交互
)

// 初始化主题索引映射
func initTopicIndexMap(filePath string) error {
	open, err := os.Open(filePath + "topic") //打开文件filePath
	if err != nil {                          //打开错误
		return err
	}
	scanner := bufio.NewScanner(open) //逐行扫描的工具
	topicTmpMap := make(map[int64]*Topic) //值是指向Topic结构体的指针
	for scanner.Scan() {
		text := scanner.Text() //保存每行内容
		var topic Topic
		if err := json.Unmarshal([]byte(text), &topic); err != nil {
			return err //json.Unmarshal将text解析为Topic结构体
		}
		topicTmpMap[topic.Id] = &topic //保存解析的结构体保
	}
	topicIndexMap = topicTmpMap //映射赋值给全局变量
	return nil
}

查询 

👇

package main

import (
	"bufio"         //缓冲读取
	"encoding/json" //处理JSON数据
	"os"            //操作系统交互
	"sync"
)

// 声明一个全局变量 topicIndexMap,用于存储主题索引映射
var topicIndexMap map[int64]*Topic

// Topic 定义 Topic 结构体,包含了主题的属性
type Topic struct {
	Id         int64  `json:"id"`
	Title      string `json:"title"`
	Content    string `json:"content"`
	CreateTime int64  `json:"create_time"`
}

// TopicDao 定义 TopicDao 结构体
type TopicDao struct {
}

var (
	topicDao  *TopicDao
	topicOnce sync.Once
)

// NewTopicDaoInstance 函数返回一个 TopicDao 实例
func NewTopicDaoInstance() *TopicDao {
	topicOnce.Do(
		func() {
			topicDao = &TopicDao{}
		})
	return topicDao
}

// QueryTopicById 方法根据 id 查询并返回对应的 Topic 实例
func (*TopicDao) QueryTopicById(id int64) *Topic {
	return topicIndexMap[id]
}

// 初始化主题索引映射
func initTopicIndexMap(filePath string) error {
	open, err := os.Open(filePath + "topic") //打开文件filePath
	if err != nil {                          //打开错误
		return err
	}
	scanner := bufio.NewScanner(open)     //逐行扫描的工具
	topicTmpMap := make(map[int64]*Topic) //值是指向Topic结构体的指针
	for scanner.Scan() {
		text := scanner.Text() //保存每行内容
		var topic Topic
		if err := json.Unmarshal([]byte(text), &topic); err != nil {
			return err //json.Unmarshal将text解析为Topic结构体
		}
		topicTmpMap[topic.Id] = &topic //保存解析的结构体保
	}
	topicIndexMap = topicTmpMap //映射赋值给全局变量
	return nil
}

💪Service

定义两个实体👇

流程👇

流程代码👇

//Do 方法执行查询页面信息的流程,返回 PageInfo 实例和错误信息
func (f *QueryPageInfoFlow) Do() (*PageInfo, error) {
	if err := f.checkParam(); err != nil { //参数校验
		return nil, err //校验失败
	}
	if err := f.prepareInfo(); err != nil { //准备数据
		return nil, err
	}
	if err := f.packPageInfo(); err != nil { //组装实体
		return nil, err
	}
	return f.pageInfo, nil //返回 PageInfo 实例和 nil,操作成功
}

💪Controller

....看了一遍....

💪Router

💤运行

end....

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

realsense-viewer 不识别 T265——Realsense SDK 在 v2.54.1 版本以后不再支持T265相机的解决办法

由于T265停产,Intel RealSense™ SDK 2.0 (v2.54.1) 在该版本中移除了对T265相机的支持,以后的版本也不会支持了。为了继续使用 T265 相机,最好千万不要升级 realsense 相关的 package,但是还有新装机的需求啊。经测试Intel RealS…

深度学习Redis(4):哨兵

前言 在 Redis(3):主从复制 中曾提到,Redis主从复制的作用有数据热备、负载均衡、故障恢复等;但主从复制存在的一个问题是故障恢复无法自动化。本文将要介绍的哨兵,它基于Redis主从复制,主要作…

js中exec与match的区别

const regex1 RegExp(f(o.?),g); const str1 table foatball, fobsball; let array1; let array2; array1 regex1.exec(str1) array2 str1.match(regex1)console.log(array1, array1); console.log(array2, array2); //没有g的情况下,都是找到第一个匹配,并且如果有分组,…

【C#学习笔记】引用类型(1)

文章目录 引用类型class匿名类 记录引用相等和值相等record声明 接口delegate 委托合并委托/多路广播委托 引用类型 引用类型的变量存储对其数据(对象)的引用,而值类型的变量直接包含其数据。 对于引用类型,两种变量可引用同一对…

软件开发和测试开发选哪个更好?一文讲清!

1、岗位需求分析 随着科技的发展,软件测试领域对人才的要求越来越高,特别测试开发岗位已成行业热点关注对象。 做开发的同学也对测试开发岗位感到好奇,为什么做测试还要写代码做开发? 他们都在开发些什么软件? 到底…

【C++】开源:Eigen3矩阵与线性代数库配置使用

😏★,:.☆( ̄▽ ̄)/$:.★ 😏 这篇文章主要介绍Eigen3矩阵与线性代数库配置使用。 无专精则不能成,无涉猎则不能通。——梁启超 欢迎来到我的博客,一起学习,共同进步。 喜欢的朋友可以关注一下&…

ElementUI el-table 鼠标滚动失灵的问题及解决办法

Bug:ElementUI el-table 鼠标滚轮下滑动失灵的情况 我测出来的这个问题条件很苛刻,需要达到以下几个条件才会触发: 1.element plus(其他版本没试) 2.el-table-column组件有fixed属性时 3.template标签中有el-butto…

C++ | 位图与布隆过滤器

目录 前言 一、位图 1、位图的引入 2、位图的实现 (1)基本结构 (2)构造函数 (3)插入数据 (4)删除数据 (5)是否存在 3、位图的优缺点 4、位图的应用…

pytorch入门

详细安装教程和环境配置可以看:Python深度学习:安装Anaconda、PyTorch(GPU版)库与PyCharm_哔哩哔哩_bilibili 跟学课程:B站我是土堆 pytorch中两个实用函数: dir():打开 help():说明书…

Java POI 百万规模数据的导入和导出

目录 1、百万数据导入1.1 需求分析1.2 思路分析1.3 代码实现1.3.1 步骤分析1.3.2 自定义处理器1.3.3 自定义解析1.3.4 测试 2、百万数据导出2.1、概述2.2、解决方案分析2.3、原理分析2.4、百万数据的导出2.4.1、模拟数据2.4.2、思路分析2.4.3、代码实现2.4.4、测试结果 1、百万…

python-网络爬虫.Request

Request python中requests库使用方法详解: 一简介: Requests 是Python语言编写,基于urllib, 采用Apache2 Licensed开源协议的 HTTP 库。 与urllib相比,Requests更加方便,处理URL资源特别流畅。 可以节约我…

如何消除浮动

第一种方法: 1、创建一个general.css文件: charset "utf-8"; .clearfix:after {content: "";display: block;clear: both;} /* flex */ .flex,.flexA,.flexB,.flexC {display: flex;flex-wrap: wrap;} .flexA {justify-content: space-aroun…

iPhone 6透明屏是什么?原理、特点、优势

iPhone 6透明屏是一种特殊的屏幕技术,它能够使手机屏幕变得透明,让用户能够透过屏幕看到手机背后的物体。 这种技术在科幻电影中经常出现,给人一种未来科技的感觉。下面将介绍iPhone 6透明屏的原理、特点以及可能的应用。 iPhone 6透明屏的原…

if语句实现成绩等级判断

if语句实现成绩等级判断 案例分析代码实现小结Time 案例分析 使用键盘输入一个成绩,然后通过if判断语句实现成绩等级的判断 代码实现 import java.util.Scanner;public class DetermineDemo {public static void main(String[] args) {Scanner scanner new Scanne…

服务器硬件、部署LNMP动态网站、部署wordpress、配置web与数据库服务分离、配置额外的web服务器

day01 day01项目实战目标单机安装基于LNMP结构的WordPress网站基本环境准备配置nginx配置数据库服务部署wordpressweb与数据库服务分离准备数据库服务器迁移数据库配置额外的web服务器 项目实战目标 主机名IP地址client01192.168.88.10/24web1192.168.88.11/24web2192.168.88…

ElasticSearch可视化管理工具之ElasticHD

推荐的五种客户端 1.Elasticsearch-Head , Elasticsearch-Head 插件在5.x版本之后已不再维护,界面比较老旧。 2.cerebro 据传该插件不支持ES中5.x以上版本。 3.kinaba 功能强大,但操作复杂,以后可以考虑。 4.Dejavu 也是一个 Elas…

vue 新学习 04 css样式绑定,渲染,key的重要意义

之前的html文件如何去绑定css样式&#xff1f; 01.首先在html文件中&#xff0c;在<head>标签中&#xff0c;用<style>中去写样式&#xff0c;通过html标签(每一个标签都有这样子的属性)中的class或者是id属性来完成<style>中的描绘的样式的用。 例子&#x…

语义分割文献整理

2014年文献 1.论文题目《Semantic Image Segmentation with Deep Convolutional Nets and Fully Connected CRFs》 1.1.网络别名《DeepLabV1》 1.2.论文引用 Chen L C, Papandreou G, Kokkinos I, et al. Semantic image segmentation with deep convolutional nets and ful…

通过 CCIP 构建跨链应用(5 个案例)

Chainlink 的跨链互操作性协议&#xff08;CCIP&#xff09;是一种新的通用跨链通信协议&#xff0c;为智能合约开发人员提供了以最小化信任的方式在区块链网络之间传输数据和通证的能力。 目前&#xff0c;部署在多个区块链上的应用程序面临着资产、流动性和用户的碎片化问题…