Go 语言中一个模拟接口的工具

栏目: Go · 发布时间: 5年前

内容简介:单元测试作为一种强大的工具,可以检查代码各个方面的行为。如果对进行代码测试十分重视,那么您将会一直编写可持续、可维护的代码,并且在代码的实现过程中保持代码的完整性。依赖于抽象的、经过开发者精心设计的代码是很容易进行测试的,所以代码的可测试性也作为其质量的一个指标。如果您已经在 Go 中尝试过测试代码,您可能知道接口的巨大作用。在 Go 的标准库中,提供了一系列接口,这些接口大多数只包含一个方法,您可以使用这些接口。

Go 语言中一个模拟接口的工具

单元测试作为一种强大的工具,可以检查代码各个方面的行为。如果对进行代码测试十分重视,那么您将会一直编写可持续、可维护的代码,并且在代码的实现过程中保持代码的完整性。依赖于抽象的、经过开发者精心设计的代码是很容易进行测试的,所以代码的可测试性也作为其质量的一个指标。

如果您已经在 Go 中尝试过测试代码,您可能知道接口的巨大作用。在 Go 的标准库中,提供了一系列接口,这些接口大多数只包含一个方法,您可以使用这些接口。

Go 还有一个补充框架,用以模拟接口。同时,还有一些社区驱动的包可以完成类似的功能。他们中的大多数都可以根据给定接口,生成实现这些接口的 struct 。 对于较大的接口,或者嵌套了其他接口,使用这种方式很有效。当接口只有一个方法时,不是更有效果吗?

关于 Go 中的接口,最令人惊讶的部分是它的默认满足性。任何类型,只需要提供其签名与接口声明中的方法匹配的实现,即可以满足该接口。这种类型甚至可以是函数,如果您熟悉 net/http 包,你也可能看到其中的一种可以叫做 adapters 的类型。

// A Handler responds to an HTTP request.
type Handler interface {
    ServeHTTP(ResponseWriter, *Request)
}

// The HandlerFunc type is an adapter to allow the use of
// ordinary functions as HTTP handlers. If f is a function
// with the appropriate signature, HandlerFunc(f) is a
// Handler that calls f.
type HandlerFunc func(ResponseWriter, *Request)

// ServeHTTP calls f(w, r).
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
    f(w, r)
}

如上述代码, adapters 类型本身是一个函数类型,而且具有与接口方法声明相同的签名,它通过在对应方法中调用自身,实现了接口。这个适配器允许具有适当签名的任何函数来实现 Handler 。它作为一种模拟接口的通用工具,看起来在表驱动的测试中十分有用。例如,需要测试以下代码:

package execute

import (
	"errors"
	"fmt"
	"io"
)

var (
	// ErrExpectedError return when error is expected.
	ErrExpectedError = errors.New("expected error")
)

// Doer does some job.
type Doer interface {
	Do() (jobID int, err error)
}

// Execute executes Doer interface and handles errors.
func Execute(job Doer, w io.Writer) {
	id, err := job.Do()
	if err != nil {
		switch err {
		case ErrExpectedError:
			fmt.Fprintf(w, "%v\n", err)
		default:
			fmt.Fprintf(w, "unexpected error: %v\n", err)
		}

		return
	}

	fmt.Fprintf(w, "job %d done\n", id)
}

使用 adapter 的单元测试如下:

package execute

import (
	"bytes"
	"errors"
	"testing"

	"github.com/stretchr/testify/assert"
)

func Test_Execute(t *testing.T) {
	tests := []struct {
		name    string
		doFunc  func() (int, error)
		wantErr bool
		expect  string
	}{
		{
			name: "expected error",
			doFunc: func() (int, error) {
				return 0, ErrExpectedError
			},
			expect: "expected error\n",
		},
		{
			name: "unexpected error",
			doFunc: func() (int, error) {
				return 0, errors.New("mocked error")
			},
			expect: "unexpected error: mocked error\n",
		},
		{
			name: "job done",
			doFunc: func() (int, error) {
				return 333, nil
			},
			expect: "job 333 done\n",
		},
	}

	for _, tt := range tests {
		tt := tt
		t.Run(tt.name, func(t *testing.T) {
			t.Parallel()

			var buf bytes.Buffer
			Execute(doerFunc(tt.doFunc), &buf)

			assert.Equal(t, tt.expect, buf.String())
		})
	}
}

type doerFunc func() (int, error)

func (f doerFunc) Do() (int, error) {
	return f()
}

编写这样的 adapters 十分繁杂,所以我决定编写一个 工具 来生成代码,叫做 adapt 。使用这个工具可以对一个指定接口生成 adapters ,并且打印其输出。你所需要做的工作就是,传入一个包名和接口名来生成代码。

$ adapt io Reader
type readerFunc func([]byte) (int, error)

func (f readerFunc) Read(p []byte) (int, error) {
	return f(p)
}

也可以在包中的文件夹内部,使用 adapt 工具为包中的一些接口生成适配器。

$ cd $GOPATH/src/github.com/x/execute Doer
$ adapt Doer
type doerFunc func() (int, error)

func (f doerFunc) Do() (int, error) {
	return f()
}

也可以和一个便捷的 vim 插件 配合使用,可以再 vim 中直接调用该工具。

Go 语言中一个模拟接口的工具

希望您会发现它很有用!


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

刘强东自述

刘强东自述

刘强东 / 中信出版集团 / 2016-6-1 / 49.00

京东 1998年,京东还只是中关村一个经营光磁生意的小柜台,月营业额仅有几万元,如今则已经成长为中国营收规模超大的互联网企业,2015年全年营收1813亿,总交易额达到4627亿元; 为解决电商“最后一公里”的痛点,创立并自建B2C物流模式; 经常被争议,却始终坚持“不挣快钱”,选择上市不是因为“缺钱”,只为让合作伙伴睡得着觉,为用户和社会创造价值,由此成就让整个华尔街一片京东红......一起来看看 《刘强东自述》 这本书的介绍吧!

CSS 压缩/解压工具
CSS 压缩/解压工具

在线压缩/解压 CSS 代码

SHA 加密
SHA 加密

SHA 加密工具

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具