第四节——生成go测试代码
Posted 想学习安全的小白
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了第四节——生成go测试代码相关的知识,希望对你有一定的参考价值。
第四章——在 Go 中生成和序列化 protobuf 消息
4.1、创建工作文件
- 创建一个
sample
包来生成一些随机的笔记本电脑数据 - 创建
sample/generator.go
文件 - 创建
sample/random.go
文件
4.2、生成随机键盘
- 在
sample/generator.go
文件中一个函数NewKeyboard()
,返回一个pb.Keyboard结构体
- 在proto中我们定义Layout为枚举类型共有三个值;backlist为bool类型
- 我们将在
random.go文件
中实现随机生成枚举类型与bool类型来填充pb.Keyboard结构体
func NewKeyboard() *pb.Keyboard
keyboard := &pb.Keyboard
Layout: randomKeyboardLayout(),
Backlist: randomBool(),
return keyboard
- 在
sample/random.go
中实现randomBool()
函数- 由于函数返回值是bool值,只有两个值——true或false
- 随机生成0或1两个整数值,判断是否等于一,返回判断结果->即通过生成随机数来生成随机的bool值
- 生成随机整数使用
rand.Intn(2)
方法,生成范围为0~n-1的整数
func randomBool() bool
return rand.Intn(2) == 1
- 在
sample/random.go
中实现randomKeyboardLayout()
函数- 由于函数需要返回一个
Keyboard_Layout
类型的枚举,此枚举我们定义了三个值——pb.Keyboard_QWERTY
;pb.Keyboard_QWERTZ
;pb.Keyboard_AZERTY
- 我们随机生成0-2的整数,通过switch判断随机生成的整数来随机生成
Keyboard_Layout
类型
- 由于函数需要返回一个
func randomKeyboardLayout() pb.Keyboard_Layout
switch rand.Intn(3)
case 0:
return pb.Keyboard_QWERTY
case 1:
return pb.Keyboard_QWERTZ
default:
return pb.Keyboard_AZERTY
4.3、生成随机 CPU
- 在proto中定义cpu消息时内部定义了6个属性
message CPU
string brand = 1;
string name = 2;
uint32 number_cores = 3;
uint32 number_threads = 4;
double min_ghz = 5;
double max_ghz = 6;
- 在random.go中定义函数
randomCPUBrand()
,在Intel与AMD中随机挑选一个返回
func randomBrand() string
if rand.Intn(2) == 1
return "Intel"
else
return "AMD"
- 根据品牌名称生成随机CPU名字
func randomCPUName(brand string) string
if brand == "Intel"
return randomStringFromSet(
"Xeon E-2286M",
"Core i9-9980HK",
"Core i7-9750H",
"Core i5-9400F",
"Core i3-1005G1",
)
return randomStringFromSet(
"Ryzen 7 PRO 2700U",
"Ryzen 5 PRO 3500U",
"Ryzen 3 PRO 3200GE",
)
func randomStringFromSet(a ...string) string
n := len(a)
if n == 0
return ""
return a[rand.Intn(n)]
- 定义随机整数,待会内核数与线程数需要使用
func randomInt(min, max int) int
return min + rand.Int()%(max-min+1)
- 定义随机浮点数,频率使用
func randomFloat64(min, max float64) float64
return min + rand.Float64()*(max-min)
4.4、生成随机 GPU
- 明确GPU属性
message GPU
string brand = 1;
string name = 2;
double min_ghz = 3;
double max_ghz = 4;
Memory memory =5;
- 定义函数随机生成GPU品牌
func randomGPUBrand() string
return randomStringFromSet("Nvidia", "AMD")
- 根据品牌随机生成名称
func randomGPUName(brand string) string
if brand == "Nvidia"
return randomStringFromSet(
"RTX 2060",
"RTX 2070",
"GTX 1660-Ti",
"GTX 1070",
)
return randomStringFromSet(
"RX 590",
"RX 580",
"RX 5700-XT",
"RX Vega-56",
)
- 频率使用之前的float随机函数生成
- 剩下一个Memory属性,生成一个memory结构体进行填充
- Memory结构体字段有两个——int的value与枚举
message Memory
enum Unit
UNKNOWN = 0;
BIT = 1;
BYTE = 2;
KILOBYTE = 3;
MEGABYTE = 4;
GIGABYTE = 5;
TERABYTE = 6;
uint64 value = 1;
Unit unit = 2;
- 创建Memory结构体进行填充时枚举不随机,直接指定
func NewGPU() *pb.GPU
brand := randomGPUBrand()
name := randomGPUName(brand)
minGhz := randomFloat64(1.0, 1.5)
maxGhz := randomFloat64(minGhz, 2.0)
memGB := randomInt(2, 6)
gpu := &pb.GPU
Brand: brand,
Name: name,
MinGhz: minGhz,
MaxGhz: maxGhz,
Memory: &pb.Memory
Value: uint64(memGB),
Unit: pb.Memory_GIGABYTE,
,
return gpu
4.5、生成随机 RAM
func NewMemory() *pb.Memory
memGB := randomInt(4, 64)
ram := &pb.Memory
Value: uint64(memGB),
Unit: pb.Memory_GIGABYTE,
return ram
4.6、生成生成随机存储
- 明确存储属性
message Storage
enum Driver
UNKNOWN = 0;
HDD = 1;
SSD = 2;
Driver driver = 1;
Memory memory = 2;
- 我们将定义 2 个不同的函数:1 个用于 SSD,1 个用于 HDD
- SSD
func NewSSD() *pb.Storage
memGB := randomInt(128, 1024)
ssd := &pb.Storage
Driver: pb.Storage_SSD,
Memory: &pb.Memory
Value: uint64(memGB),
Unit: pb.Memory_GIGABYTE,
,
return ssd
- HHD
func NewHDD() *pb.Storage
memTB := randomInt(1, 6)
hdd := &pb.Storage
Driver: pb.Storage_HDD,
Memory: &pb.Memory
Value: uint64(memTB),
Unit: pb.Memory_TERABYTE,
,
return hdd
4.7、生成随机屏幕
- 明确screen属性
message Screen
message Resolution
uint32 width=1;
uint32 height=2;
enum Panel
UNKNOW=0;
IPS=1;
OLED=2;
float size_inch=1;
Resolution resolution=2;
Panel panel=3;
bool multitouch=4;
- 定义一个
randomFloat32
函数用于生成随机尺寸
func randomFloat32(min, max float32) float32
return min + rand.Float32()*(max-min)
- 定义一个
randomScreenResolution
函数用于生成随机分辨率
func randomScreenResolution() *pb.Screen_Resolution
height := randomInt(1080, 4320)
width := height * 16 / 9
resolution := &pb.Screen_Resolution
Width: uint32(width),
Height: uint32(height),
return resolution
- 生成面板属性,由于只有两个值枚举,所以通过生成0~1随机整数来随机获取面板枚举值
func randomScreenPanel() pb.Screen_Panel
if rand.Intn(2) == 1
return pb.Screen_IPS
return pb.Screen_OLED
- 触摸属性是bool值,可以使用前面的随机bool函数来生产
- 最后完整的屏幕生成函数
func NewScreen() *pb.Screen
screen := &pb.Screen
SizeInch: randomFloat32(13, 17),
Resolution: randomScreenResolution(),
Panel: randomScreenPanel(),
Multitouch: randomBool(),
return screen
4.8、生成随机笔记本电脑
- 明确laptop属性
message Laptop
string id=1;
string brand=2;
string name=3;
CPU cpu=4;
Memory memory=5;
repeated GPU gpu=6;
repeated Storage storage=7;
Screen screen=8;
Keyboard keyboard=9;
oneof weight
double weight_kg=10;
double weight_lb=11;
double price_usd = 12;
uint32 release_year = 13;
- ID属性需要一个int类型,我们这里使用randomID() 函数随机生成UUID
- randomID() 函数中使用google/uuid插件来生成UUID,所以需要执行命令:
go get -u github.com/google/uuid
- randomID() 函数中使用google/uuid插件来生成UUID,所以需要执行命令:
func randomID() string
return uuid.New().String()
- 定义函数生成笔记本电脑品牌和名称
func randomLaptopBrand() string
return randomStringFromSet("Apple", "Dell", "Lenovo")
func randomLaptopName(brand string) string
switch brand
case "Apple":
return randomStringFromSet("Macbook Air", "Macbook Pro")
case "Dell":
return randomStringFromSet("Latitude", "Vostro", "XPS", "Alienware")
default:
return randomStringFromSet("Thinkpad X1", "Thinkpad P1", "Thinkpad P53")
- 使用之前定义的New函数,生成Laptop对应字段,注意GPU与Storage属性,是一个数组;注意weight属性二选一
- GPU 应该是一个值列表,所以在这里定义了一个切片。假设现在只有 1 个 GPU。存储类似,但这次将添加 2 个项目:1 个用于 SSD,另一个用于 HDD。
func NewLaptop() *pb.Laptop
brand := randomLaptopBrand()
name := randomLaptopName(brand)
laptop := &pb.Laptop
Id: randomID(),
Brand: brand,
Name: name,
Cpu: NewCPU(),
Memory: NewMemory(),
Gpu: []*pb.GPUNewGPU(),
Storage: []*pb.StorageNewSSD(), NewHDD(),
Screen: NewScreen(),
Keyboard: NewKeyboard(),
Weight: &pb.Laptop_WeightKg
WeightKg: randomFloat64(1.0, 3.0),
,
PriceUsd: randomFloat64(1500, 3500),
ReleaseYear: uint32(randomInt(2015, 2019)),
return laptop
4.9、序列化 protobuf 消息
- 创建一个新
serializer
文件夹,创建文件serializer/file.go
- 编写
WriteProtobufToBinaryFile
函数将消息转化为二进制文件
func WriteProtobufToBinaryFile(message proto.Message, filename string) error
return nil
- 我们首先调用
proto.Marshal
将消息序列化为二进制。如果发生错误,我们只需将其包装并返回给调用者。
func WriteProtobufToBinaryFile(message proto.Message, filename string) error
data, err := proto.Marshal(message)
if err != nil
return fmt.Errorf("cannot marshal proto message to binary: %w", err)
return nil
- 我们使用
ioutil.WriteFile()
函数将数据保存到指定的文件名
func WriteProtobufToBinaryFile(message proto.Message, filename string) error
data, err := proto.Marshal(message)
if err != nil
return fmt.Errorf("cannot marshal proto message to binary: %w", err)
err = ioutil.WriteFile(filename, data, 0644)
if err != nil
return fmt.Errorf("cannot write binary data to file: %w", err)
return nil
4.10、编写单元测试
- 创建文件
serializer/file_test.go
,使用包名package serializer_test
func TestFileSerializer(t *testing.T)
t.Parallel()
- 创建
tmp
文件夹用于存放生成的二进制文件
func TestFileSerializer(t *testing.T)
t.Parallel()
binaryFile := "../tmp/laptop.bin"
- 使用
NewLaptop()
函数创建一个新的laptop1
. 并调用该WriteProtobufToBinaryFile()
函数将其保存到laptop.bin
文件中
func TestFileSerializer(t *testing.T)
t.Parallel()
binaryFile := "../tmp/laptop.bin"
laptop1 := sample.NewLaptop()
err := serializer.WriteProtobufToBinaryFile(laptop1, binaryFile)
- 检查此错误是否为
nil
,需要引入包go get github.com/stretchr/testify
,使用方式:require.NoError(t, err)
func TestFileSerializer(t *testing.T)
t.Parallel()
binaryFile := "../tmp/laptop.bin"
laptop1 := sample.NewLaptop()
err := serializer.WriteProtobufToBinaryFile(laptop1, binaryFile)
require.NoError(t, err)
- 编写另一个函数
ReadProtobufFromBinaryFile()
来读取该二进制文件,在file.go文件中
func ReadProtobufFromBinaryFile(filename string, message proto.Message) error
return nil
- 使用
ioutil.ReadFile()
从文件中读取二进制数据。然后调用proto.Unmarshal()
将二进制数据反序列化为 protobuf 消息
func ReadProtobufFromBinaryFile(filename string, message proto.Message) error
data, err := ioutil.ReadFile(filename)
if err != nil
return fmt.Errorf("cannot read binary data from file: %w", err)
err = proto.Unmarshal(data, message)
if err != nil
return fmt.Errorf("cannot unmarshal binary to proto message: %w", err)
return nil
- 在单元测试中,我将定义一个新
laptop2
对象,并调用ReadProtobufFromBinaryFile()
以将文件数据读入该对象
laptop2 := &pb.Laptop
err = serializer.ReadProtobufFromBinaryFile(binaryFile, laptop2)
require.NoError(t, err)
- 使用包
proto.Equal
检查laptop1与laptop2是否一致,使用require.True()
判断是否返回true
func TestFileSerializer(t *testing.T)
t.Parallel()
binaryFile := "../tmp/laptop.bin"
laptop1 := sample.NewLaptop()
err := serializer.WriteProtobufToBinaryFile(laptop1, binaryFile)
require.NoError(t, err)
laptop2 := &pb.Laptop
err = serializer.ReadProtobufFromBinaryFile(binaryFile, laptop2)
require.NoError(t, err)
require.True(t, proto.Equal(laptop1, laptop2))
以上是关于第四节——生成go测试代码的主要内容,如果未能解决你的问题,请参考以下文章
(王道408考研数据结构)第六章图-第四节1:最小生成树之普利姆算法(思想代码演示答题规范)