# 4.6 如何测试

类型检查的测试比语法分析稍稍复杂一些，因为首先类型检查要依赖语法分析的输出，其次需要对类型检查器及整个编译环境进行初始化。

可以从`check_test.go`文件切入学习现有的测试代码， `examples`, `fixedbugs`以及`testdata`三个目录下包含大量的测试用例，对应的测试入口函数如下：

```go
func TestTestdata(t *testing.T)  { DefPredeclaredTestFuncs(); testDir(t, 75, "testdata") }
func TestExamples(t *testing.T)  { testDir(t, 0, "examples") }
func TestFixedbugs(t *testing.T) { testDir(t, 0, "fixedbugs") }
```

{% hint style="info" %}
Go 支持两种测试策略：黑盒测试与白盒测试。黑盒测试关注点在功能层面，测试代码仅允许访问被测试包的 Exported API ；而白盒测试关注点在实现层面，测试代码需要访问被测试包的所有内容。

Go 规范中约定测试文件与被测试文件在同一目录下，那么如何限制测试代码的访问权限呢？答案是将测试文件申明为不同的包。我们知道 Go 要求同一个目录下所有文件只能隶属于同一个包，但测试文件例外，测试文件的包名可以申明为`package xxx_test`, 其中 xxx 是被测试文件的包名，这样测试文件就处在单独的包中，以此达到黑盒测试的目的。
{% endhint %}

类型检查器采用的测试策略是黑盒测试，为了能够完成初始化，测试代码中已经实现了很多必要的辅助模块，例如`defaultImporter`, 同时语法解析也单独进行了封装。我们可以在`check_test.go`自定义如下方法来做测试：

```go
func TestTypecheck(t *testing.T) {
	code := `
package p

type A struct {}

func (this *A) name() {}

type B *A

func main() {
   var b B

  b.name()
}
`
	f, err := parseSrc("testTypecheckConst", code)

	if err != nil {
		panic(err)
	}

	// Dump 出语法树
	syntax.Fdump(os.Stdout, f)

	var conf Config
	conf.Trace = true
	conf.Importer = defaultImporter()
	conf.Error = func(err error) {
		fmt.Printf("Typecheck Error: %v\n", err)
	}

	info := Info{
		Types:      make(map[syntax.Expr]TypeAndValue),
		Defs:       make(map[*syntax.Name]Object),
		Uses:       make(map[*syntax.Name]Object),
		Selections: make(map[*syntax.SelectorExpr]*Selection),
		Implicits:  make(map[syntax.Node]Object),
		Scopes:     make(map[syntax.Node]*Scope),
		Inferred:   make(map[syntax.Expr]Inferred),
	}

	conf.Check("<no package>", []*syntax.File{f}, &info)
}
```

因为设置了`conf.Trace = true`, 我们将看到非常详细的检查结果，这里仅仅截取 main 函数的函数体的检查结果作为演示：

> ```
> == processDelayed ==
> testTypecheckConst:7:23:	--- name: func()
> testTypecheckConst:8:1:	--- <end>
> testTypecheckConst:12:13:	--- main: func()
> testTypecheckConst:13:10:	type B
> testTypecheckConst:13:10:	=> B (under = *A) // *types2.Named
> testTypecheckConst:15:7:	expr b.name()
> testTypecheckConst:15:2:	.  expr b.name
> testTypecheckConst:15:1:	.  .  expr b
> testTypecheckConst:15:1:	.  .  => b (variable of type B)
> testTypecheckConst:15:3:	.  .  ERROR: b.name undefined (type B has no field or method name)
> Typecheck Error: testTypecheckConst:15:3: b.name undefined (type B has no field or method name)
> testTypecheckConst:15:2:	.  => b.name (invalid operand)
> testTypecheckConst:15:7:	=> b.name() (invalid operand)
> testTypecheckConst:16:1:	--- <end>
> ```

对于方法调用`b.name()`, 类型检查器合理地报告了错误。

这样我们就可以任意修改源代码，并通过 UT 的方式查看效果，或者通过调试器查看每个细节是如何工作的了。


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://gocompiler.shizhz.me/golang-bian-yi-qi-lei-xing-jian-cha/4.6-ru-he-ce-shi.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
