Compartir tecnología

Prueba de conocimiento-Prueba de ejemplo

2024-07-12

한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina

Se recomienda leer primero: https://blog.csdn.net/a18792721831/article/details/140062769

Mecanismo de trabajo de prueba de conocimiento

1. Definición

La prueba de muestra debe garantizar que el archivo de prueba termine con_test.gofin.
El método de prueba debe serExampleXxxcomienzo.
Los archivos de prueba pueden estar en el mismo directorio que el código fuente o en un directorio separado.
Detectar formato de salida de una sola línea como // Salida: <cadena esperada>
El formato para detectar salida de varias líneas es // Salida: n <cadena esperada> n <cadena esperada> n, cada cadena esperada ocupa una línea
El formato para detectar resultados desordenados es // Salida desordenada: n <cadena esperada> n <cadena esperada> n <cadena esperada>, cada cadena esperada ocupa una línea
La cadena de prueba ignorará automáticamente los espacios en blanco antes y después de la cadena.
Si no hay representación de Salida en la función de prueba, la función de prueba no se ejecutará.

2. Ejemplo

En la salida de la cadena, puede ser una línea, pueden ser varias líneas o puede estar desordenada.
La función es la siguiente:

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)
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

Luego use la prueba de ejemplo.

func ExampleHello() {
	Hello()
	// Output: Hello
}

func ExampleHelloTwo() {
	HelloTwo()
	// Output:
	// Hello
	// hi
}

func ExampleHelloMore() {
	HelloMore()
	// Unordered output:
	// Hello
	// hi
	// 你好
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

usargo test -v Ejecute pruebas de muestra, -v indica los resultados de salida de la consola
Insertar descripción de la imagen aquí

3. Estructura de datos

Cada prueba tiene una estructura de datos para transportarla después de la compilación. Para la prueba de ejemplo, es InternalExample:

type InternalExample struct {
	Name      string // 测试名称
	F         func() // 测试函数
	Output    string // 期望字符串
	Unordered bool // 输出是否无序
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

Por ejemplo, el siguiente caso:

func ExampleHelloMore() {
	HelloMore()
	// Unordered output:
	// Hello
	// hi
	// 你好
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

Después de la compilación, los miembros de la estructura de datos.

InternalExample.Name = "ExampleHelloMore"
InternalExample.F = ExampleHelloMore()
InternalExample.Output = "hellonhin你好n"
InternalExample.Unordered = true
  • 1
  • 2
  • 3
  • 4

4. Proceso de compilación

En el artículo: Mecanismo de trabajo de prueba de conocimiento

Mecanismo de trabajo de prueba de conocimiento

, sabemos que al compilar se llamarásrc/cmd/go/internal/load/test.go:528
Insertar descripción de la imagen aquí

En carga, se procesarán cuatro tipos de pruebas por separado: prueba unitaria, prueba de rendimiento, prueba principal y prueba de muestra.
Insertar descripción de la imagen aquí

Al procesar el archivo de prueba, verifique si el comentario contiene resultados
Insertar descripción de la imagen aquí

Y encapsular metadatos
Insertar descripción de la imagen aquí

Una vez encapsulados los metadatos, los metadatos se utilizarán para representar la plantilla, generar la entrada principal y representar el segmento InternalExample al mismo tiempo.
Insertar descripción de la imagen aquí

5. Proceso de ejecución

Cuando se ejecute, se ejecutará.testing.M.Run, se ejecutará en EjecutarrunExamples
Insertar descripción de la imagen aquí

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
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
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
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
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
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34