第四节——生成go测试代码

Posted 想学习安全的小白

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了第四节——生成go测试代码相关的知识,希望对你有一定的参考价值。

第四章——在 Go 中生成和序列化 protobuf 消息

4.1、创建工作文件

  1. 创建一个sample包来生成一些随机的笔记本电脑数据
  2. 创建sample/generator.go文件
  3. 创建sample/random.go文件

4.2、生成随机键盘

  1. 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

  1. sample/random.go中实现randomBool()函数
    • 由于函数返回值是bool值,只有两个值——true或false
    • 随机生成0或1两个整数值,判断是否等于一,返回判断结果->即通过生成随机数来生成随机的bool值
    • 生成随机整数使用rand.Intn(2)方法,生成范围为0~n-1的整数
func randomBool() bool 
	return rand.Intn(2) == 1

  1. sample/random.go中实现randomKeyboardLayout()函数
    • 由于函数需要返回一个Keyboard_Layout类型的枚举,此枚举我们定义了三个值——pb.Keyboard_QWERTYpb.Keyboard_QWERTZpb.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

  1. 在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;

  1. 在random.go中定义函数randomCPUBrand(),在Intel与AMD中随机挑选一个返回
func randomBrand() string 
	if rand.Intn(2) == 1 
		return "Intel"
	 else 
		return "AMD"
	

  1. 根据品牌名称生成随机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)]

  1. 定义随机整数,待会内核数与线程数需要使用
func randomInt(min, max int) int 
    return min + rand.Int()%(max-min+1)

  1. 定义随机浮点数,频率使用
func randomFloat64(min, max float64) float64 
	return min + rand.Float64()*(max-min)

4.4、生成随机 GPU

  1. 明确GPU属性
message GPU 
  string brand = 1;
  string name = 2;
  double min_ghz = 3;
  double max_ghz = 4;
  Memory memory =5;

  1. 定义函数随机生成GPU品牌
func randomGPUBrand() string 
	return randomStringFromSet("Nvidia", "AMD")

  1. 根据品牌随机生成名称
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",
	)

  1. 频率使用之前的float随机函数生成
  2. 剩下一个Memory属性,生成一个memory结构体进行填充
  3. 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;
  
  1. 创建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、生成生成随机存储

  1. 明确存储属性
message Storage 
    enum Driver 
      UNKNOWN = 0;
      HDD = 1;
      SSD = 2;
    
  
    Driver driver = 1;
    Memory memory = 2;
  
  1. 我们将定义 2 个不同的函数:1 个用于 SSD,1 个用于 HDD
  2. 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

  1. 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、生成随机屏幕

  1. 明确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;

  1. 定义一个randomFloat32函数用于生成随机尺寸
func randomFloat32(min, max float32) float32 
    return min + rand.Float32()*(max-min)

  1. 定义一个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

  1. 生成面板属性,由于只有两个值枚举,所以通过生成0~1随机整数来随机获取面板枚举值
func randomScreenPanel() pb.Screen_Panel 
    if rand.Intn(2) == 1 
        return pb.Screen_IPS
    
    return pb.Screen_OLED

  1. 触摸属性是bool值,可以使用前面的随机bool函数来生产
  2. 最后完整的屏幕生成函数
func NewScreen() *pb.Screen 
    screen := &pb.Screen
        SizeInch:   randomFloat32(13, 17),
        Resolution: randomScreenResolution(),
        Panel:      randomScreenPanel(),
        Multitouch: randomBool(),
    

    return screen

4.8、生成随机笔记本电脑

  1. 明确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;

  1. ID属性需要一个int类型,我们这里使用randomID() 函数随机生成UUID
    • randomID() 函数中使用google/uuid插件来生成UUID,所以需要执行命令:go get -u github.com/google/uuid
func randomID() string 
    return uuid.New().String()

  1. 定义函数生成笔记本电脑品牌和名称
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")
	

  1. 使用之前定义的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 消息

  1. 创建一个新serializer文件夹,创建文件serializer/file.go
  2. 编写WriteProtobufToBinaryFile函数将消息转化为二进制文件
func WriteProtobufToBinaryFile(message proto.Message, filename string) error 
	return nil

  1. 我们首先调用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

  1. 我们使用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、编写单元测试

  1. 创建文件serializer/file_test.go,使用包名package serializer_test
func TestFileSerializer(t *testing.T) 
    t.Parallel()

  1. 创建tmp文件夹用于存放生成的二进制文件
func TestFileSerializer(t *testing.T) 
	t.Parallel()

	binaryFile := "../tmp/laptop.bin"

  1. 使用NewLaptop()函数创建一个新的laptop1. 并调用该WriteProtobufToBinaryFile()函数将其保存到laptop.bin文件中
func TestFileSerializer(t *testing.T) 
	t.Parallel()

	binaryFile := "../tmp/laptop.bin"

	laptop1 := sample.NewLaptop()
	err := serializer.WriteProtobufToBinaryFile(laptop1, binaryFile)

  1. 检查此错误是否为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)

  1. 编写另一个函数ReadProtobufFromBinaryFile()来读取该二进制文件,在file.go文件中
func ReadProtobufFromBinaryFile(filename string, message proto.Message) error 
	return nil

  1. 使用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

  1. 在单元测试中,我将定义一个新laptop2对象,并调用ReadProtobufFromBinaryFile()以将文件数据读入该对象
laptop2 := &pb.Laptop
    err = serializer.ReadProtobufFromBinaryFile(binaryFile, laptop2)
    require.NoError(t, err)
  1. 使用包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测试代码的主要内容,如果未能解决你的问题,请参考以下文章

第四节——生成go测试代码

(王道408考研数据结构)第六章图-第四节1:最小生成树之普利姆算法(思想代码演示答题规范)

(王道408考研数据结构)第六章图-第四节2:最小生成树之克鲁斯卡尔算法(思想代码演示答题规范)

第四节——配置文件解释

实现和测试-第四节:集成测试

opencv学习-第四节:图像对象的创建与赋值