2024-07-12
한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina
Il est recommandé de lire en premier : https://blog.csdn.net/a18792721831/article/details/140062769
Mécanisme de travail des tests Go-Knowledge
L'exemple de test doit garantir que le fichier de test se termine par_test.go
fin.
La méthode d'essai doit êtreExampleXxx
début.
Les fichiers de test peuvent se trouver dans le même répertoire que le code source ou dans un répertoire distinct.
Détecter le format de sortie sur une seule ligne comme // Sortie : <chaîne attendue>
Le format de détection de la sortie multiligne est // Sortie : n <chaîne attendue> n <chaîne attendue> n, chaque chaîne attendue occupe une ligne
Le format de détection de la sortie non ordonnée est // Sortie non ordonnée : n <chaîne attendue> n <chaîne attendue> n <chaîne attendue>, chaque chaîne attendue occupe une ligne
La chaîne de test ignorera automatiquement les espaces avant et après la chaîne.
S'il n'y a pas de représentation de sortie dans la fonction de test, la fonction de test ne sera pas exécutée.
Dans la sortie de chaîne, il peut s'agir d'une seule ligne, de plusieurs lignes ou d'un désordre.
La fonction est la suivante :
func Hello() {
fmt.Println("Hello")
}
func HelloTwo() {
fmt.Println("Hello")
fmt.Println("hi")
}
func HelloMore() {
m := make(map[string]string)
m["hello"] = "Hello"
m["hi"] = "hi"
m["你好"] = "你好"
for _, v := range m {
fmt.Println(v)
}
}
Utilisez ensuite l'exemple de test
func ExampleHello() {
Hello()
// Output: Hello
}
func ExampleHelloTwo() {
HelloTwo()
// Output:
// Hello
// hi
}
func ExampleHelloMore() {
HelloMore()
// Unordered output:
// Hello
// hi
// 你好
}
utilisergo test -v
Exécuter des exemples de tests, -v indique les résultats de sortie de la console
Chaque test a une structure de données pour le porter après compilation. Pour l'exemple de test, il s'agit de InternalExample :
type InternalExample struct {
Name string // 测试名称
F func() // 测试函数
Output string // 期望字符串
Unordered bool // 输出是否无序
}
Par exemple, le cas suivant :
func ExampleHelloMore() {
HelloMore()
// Unordered output:
// Hello
// hi
// 你好
}
Après compilation, les membres de la structure de données
InternalExample.Name = "ExampleHelloMore"
InternalExample.F = ExampleHelloMore()
InternalExample.Output = "hellonhin你好n"
InternalExample.Unordered = true
Dans l'article: Mécanisme de travail des tests Go-Knowledge
, on sait qu'à la compilation, il s'appellerasrc/cmd/go/internal/load/test.go:528
En charge, quatre types de tests seront traités séparément : test unitaire, test de performances, test principal et test d'échantillon.
Lors du traitement du fichier de test, vérifiez si le commentaire contient une sortie
Et encapsuler les métadonnées
Une fois les métadonnées encapsulées, les métadonnées seront utilisées pour restituer le modèle, générer l'entrée principale et restituer la tranche InternalExample en même temps.
Une fois exécuté, il sera exécutétesting.M.Run
, sera exécuté dans RunrunExamples
func runExamples(matchString func(pat, str string) (bool, error), examples []InternalExample) (ran, ok bool) {
ok = true
var eg InternalExample
// 对每个实例测试进行执行
for _, eg = range examples {
// 是否匹配
matched, err := matchString(*match, eg.Name)
if err != nil {
fmt.Fprintf(os.Stderr, "testing: invalid regexp for -test.run: %sn", err)
os.Exit(1)
}
if !matched {
continue
}
ran = true
// 执行
if !runExample(eg) {
ok = false
}
}
return ran, ok
}
func runExample(eg InternalExample) (ok bool) {
// 附加输出
if *chatty {
fmt.Printf("=== RUN %sn", eg.Name)
}
// 获取标准输出
stdout := os.Stdout
// 新建管道,将标准输出拷贝一份
r, w, err := os.Pipe()
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
os.Stdout = w
// 创建一个管道,用于接收标准输出返回的字符串
outC := make(chan string)
// 在一个单独的 goroutine 中处理拷贝的输出
go func() {
var buf strings.Builder
_, err := io.Copy(&buf, r)
r.Close()
if err != nil {
fmt.Fprintf(os.Stderr, "testing: copying pipe: %vn", err)
os.Exit(1)
}
outC <- buf.String()
}()
finished := false
start := time.Now()
// 在延迟函数中,读取管道中的数据
defer func() {
timeSpent := time.Since(start)
w.Close()
os.Stdout = stdout
// 获取标准输出
out := <-outC
err := recover()
// 调用 processRunResult 进行比较
ok = eg.processRunResult(out, timeSpent, finished, err)
}()
// 执行示例函数,也就是目标函数
eg.F()
finished = true
return
}
func (eg *InternalExample) processRunResult(stdout string, timeSpent time.Duration, finished bool, recovered interface{}) (passed bool) {
passed = true
dstr := fmtDuration(timeSpent)
var fail string
// 标准输出,去除空字符,这也是为何实例测试中会忽略空白字符
got := strings.TrimSpace(stdout)
// 期望输出
want := strings.TrimSpace(eg.Output)
// 是否乱序
if eg.Unordered {
// 先排序,然后字符串比较
if sortLines(got) != sortLines(want) && recovered == nil {
fail = fmt.Sprintf("got:n%snwant (unordered):n%sn", stdout, eg.Output)
}
} else {
if got != want && recovered == nil {
fail = fmt.Sprintf("got:n%snwant:n%sn", got, want)
}
}
if fail != "" || !finished || recovered != nil {
fmt.Printf("--- FAIL: %s (%s)n%s", eg.Name, dstr, fail)
passed = false
} else if *chatty {
fmt.Printf("--- PASS: %s (%s)n", eg.Name, dstr)
}
if recovered != nil {
// Propagate the previously recovered result, by panicking.
panic(recovered)
}
if !finished && recovered == nil {
panic(errNilPanicOrGoexit)
}
return
}