> For the complete documentation index, see [llms.txt](https://gocompiler.shizhz.me/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://gocompiler.shizhz.me/golang-bian-yi-qi-lei-xing-jian-cha/4.6-ru-he-ce-shi.md).

# 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 的方式查看效果，或者通过调试器查看每个细节是如何工作的了。
