Golang-单元测试

单元测试

针对使用传统方式测试代码块的不足:
1)测试代码块需要在main函数中去调用,需要修改main函数,若项目正在运行,就可能去停止项目,不方便
2)不利于管理,当需要测试多个函数或多个模块时,都需要在main函数,不利于我们管理和清晰思路
3)引出单元测试。testing测试框架,很好解决上述问题。

1. 基本介绍

Go语言自带有一个轻量级的测试框架testing和自带的 go test命令实现单元测试和性能测试。testing框架和其它语言中的测试框架类似,可以基于这个框架写针对相应函数的测试用例,也可以基于该框架写相应的压力测试用例。通过单元测试,可以解决:
1)确保每个函数的是可运行,并且运行结果是正确
2)确保代码的性能是好的
3)单元测试能及时的发现程序设计或实现的逻辑错误,使得问题及时暴露,便于问题的定位和解决;而性能测试的重点在于发现程序设计上的问题,确保程序在高并发的情况下还能保持稳定。

2.单元测试入门案例

Golang中提供了testing包,该包提供对Go包的自动化测试的支持。通过go test命令能够自动执行如下形式的任何函数:

func TestXxx(*testing.T)

  1. 其中 Xxx 可以是任何字母数字字符串(但第一个字母不能是 [a-z],且必须大写),用于识别测试程序。
    2.要编写一个新的测试套件,需要创建一个名称以 _test.go 结尾的文件,该文件包含 TestXxx 函数,如上所述。 将该文件放在与被测试的包相同的包中。该文件将被排除在正常的程序包之外,但在运 行 “go test” 命令时将被包含。

测试原理图:
在这里插入图片描述

入门案例:
使用到testing包中的func (*T) Logffunc (*T) Fatalf

func (*T) Logf
func (c *T) Logf(format string, args …interface{})
Log 使用与 Printf 相同的格式化语法对它的参数进行格式化,然后将格式化后的文本记录到错误日志里面。
1)对于测试来说,Logf 产生的格式化文本只会在测试失败或者设置了 -test.v 标志的情况下被打印出来;

func (*T) Fatalf
func (c *T) Fatalf(format string, args …interface{})
调用 Fatalf 相当于在调用 Logf 之后调用 FailNow 。

func (*T) FailNow
func (c *T) FailNow()
将当前测试标识为失败并停止执行该测试,在此之后,测试过程将在下一个测试或者下一个基准测试中继续。

案例代码:
被测试函数所在文件cal.go

package cal

func addUpper(num int) int {
	res := 0
	for i := 1; i <= num; i++ {
		res += i
	}
	return res
}

func GetSub(n1, n2 int) int {
	res := n2 - n1//计划是n1-n2 故意写错为n2-n1
	return res
}

测试函数cal_test.go

package cal

import "testing"

// 编写测试案案例,测试addUpper是否正确
func TestAddupper(t *testing.T) {
	// 调用
	res := addUpper(10)
	if res != 55 {
		t.Fatalf("addUpper(10) 执行错误,期望值=%v,实际值=%v\n", 55, res)
	}
	// 如果正确,输出日志
	t.Logf("addUpper(10) 执行正确...")
}

func TestGetSub(t *testing.T) {
	res := GetSub(10, 5)
	if res != 5 {
		t.Fatalf("GetSub(10,5) 执行错误,期望值=%v,实际值=%v\n", 5, res)
	}
	t.Logf("GetSub(10,5) 执行正确")

}

运行测试指令:
(1)cmd>go test (如果运行正确,无日志,错误时输出日志)
(2)cmd> go test -v (运行正确或错误都输出日志)
(3)PASS表示测试运行成功,FAIL表示测试失败
(4)测试单个文件,需要带上被测试的原文件:go test -v cal_test.go cal.go
(5)测试单个方法go test -v -run TestAddUpper

Output:
E:\goproject\src\go_code\chapter14\testdemo01>go test -v
=== RUN TestAddupper
cal_test.go:13: addUpper(10) 执行正确…
— PASS: TestAddupper (0.00s)
=== RUN TestGetSub
cal_test.go:19: GetSub(10,5) 执行错误,期望值=5,实际值=-5
— FAIL: TestGetSub (0.00s)
FAIL
exit status 1
FAIL go_code/chapter14/testdemo01 0.489s
说明cal.go中GetSub()函数有问题

OutPut
E:\goproject\src\go_code\chapter14\testdemo01>go test
— FAIL: TestGetSub (0.00s)
cal_test.go:19: GetSub(10,5) 执行错误,期望值=5,实际值=-5
FAIL
exit status 1
FAIL go_code/chapter14/testdemo01 0.247s
使用go test命令执行,则出现错误才会显示t.Logf()

案例2:
检测josn序列化和反序列化是否正确。
1)使用ioutil.WriteFile()函数将json字符串写入文件

func WriteFile(filename string, data []byte, perm os.FileMode) error
函数向filename指定的文件中写入数据。如果文件不存在将按给出的权限创建文件,否则在写入数据之前清空文件。因此使用时需谨慎。

2)使用ioutil.ReadFile()函数读取文件中的json信息

func ReadFile(filename string) ([]byte, error)
ReadFile 从filename指定的文件中读取数据并返回文件的内容。成功的调用返回的err为nil而非EOF。因为本函数定义为读取整个文件,它不会将读取返回的EOF视为应报告的错误。

操作代码:
被测文件monster.go

package monster

import (
	"encoding/json"
	"io/ioutil"
	"log"
)

type Monster struct {
	Name  string `json:"name"`
	Age   int    `json:"age,omitempty"`
	Skill string `json:"skill,omitempty"`
}

func (m *Monster) Store() error {
	data, err := json.Marshal(m)
	if err != nil {
		log.Fatal(err)
	}

	// 保存到文件
	filePath := "E:\\goproject\\src\\go_code\\chapter14\\monster.ser"
	// 将json信息写入文件
	ioutil.WriteFile(filePath, data, 0666)
	return err // nil
}

func (m *Monster) Restore() error {
	// 从文件中获得json信息
	filePath := "E:\\goproject\\src\\go_code\\chapter14\\monster.ser"
	data, err := ioutil.ReadFile(filePath)
	if err != nil {
		log.Fatal(err)
	}
	err = json.Unmarshal(data, m)
	if err != nil {
		log.Fatal(err)
	}
	return err
}

测试文件 monster_test.go

package monster

import (
	"testing"
)

var monster = Monster{
	Name:  "红孩儿",
	Age:   1500,
	Skill: "火尖枪",
}

// 测试用例,测试Store方法

func TestStore(t *testing.T) {

	if monster.Store() == nil {
		t.Logf("marshal successfully\n")
	} else {
		t.Logf("marshal fail,err=%v\n", monster.Store())
	}
}

// 测试用例,测试Restore方法
func TestRestore(t *testing.T) {
	monster1 := &Monster{}
	if monster1.Restore() != nil {
		t.Logf("unmarshal fail\n")
	} else {
		if *monster1 == monster {
			t.Logf("Unmarshal successfully\n")
		} else {
			if monster1.Name != monster.Name {
				t.Logf("Unmarshal fail,期望%v, 实际是%v\n", monster.Name, monster1.Name)
			}
			if monster1.Age != monster.Age {
				t.Logf("Unmarshal fail,期望%v, 实际是%v\n", monster.Age, monster1.Age)
			}
			if monster1.Skill != monster.Skill {
				t.Logf("Unmarshal fail,期望%v, 实际是%v\n", monster.Skill, monster1.Skill)
			}

		}

	}

}
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值