如何为内存数据存储创建单元测试?

huangapple go评论72阅读模式
英文:

How to create unit test for in-memory datastore?

问题

我想创建一些简单的REST API。我决定创建自己的内存数据存储,实现以下接口:

type datastore interface {
    Add(*Element) error
    Get(ElementID) (*Element, error)
    Update(*Element) error
    Delete(ElementID) error
    GetAll() []*Element
}
type Datastore struct {
    mu     sync.Mutex
    bucket map[string]*Element
}
func NewDB() *Datastore {
    return &Datastore {
        bucket: make(map[string]*Element),
    }
}

这个如何进行单元测试?

我已经创建了一些测试,看起来像这样:

func TestGetAllTODOTasks(t *testing.T) {
    ts := NewDB()
    var elem = &Element{fieldA: "A", fieldB: "B"}
    ts.Create(elem)

    want := []*Element{elem}

    if got := ts.GetAll(); !reflect.DeepEqual(got, want) {
        t.Errorf("Got %v wanted %v", got, want)
    }
}

但是当我想测试其他方法如Update时,我意识到我需要先使用Create,然后再进行更新,像这样:

func TestUpdateTODOTasks(t *testing.T) {
    ts := NewDB()
    var elem = &Element{fieldA: "A", fieldB: "B"}
    ts.Create(elem)
    if err != nil {
        t.Errorf("=> failed to create: %v", err.Error())
    }
    var updated_elem = &Element{fieldA: "A-updated", fieldB: "B"}

    err = ts.Update(updated_elem)

    if err != nil {
        t.Errorf("=> failed to update: %v", err.Error())
    }
}
英文:

I wanted to create some simple REST API. I decided to create my own in-memory datastore which implements such interface:

type datastore interface {
	Add(*Element) error
	Get(ElementID) (*Element, error)
	Update(*Element) error
	Delete(ElementID) error
	GetAll() []*Element
}
type Datastore struct {
	mu     sync.Mutex
	bucket map[string]*Element
}
func NewDB() *Datastore {
	return &Datastore {
		bucket: make(map[string]*Element),
	}
}

How should this be unit tested?

Some of the tests I managed to create looks like this:

func TestGetAllTODOTasks(t *testing.T) {
	ts := NewDB()
	var elem = &Element{fieldA : "A" , fieldB : "B"}
	ts.Create(elem)

	want := []*Element{elem}

	if got := ts.GetAll(); !reflect.DeepEqual(got, want) {
		t.Errorf("Got %v wanted %v", got, want)
	}
}

But once I wanted to test other methods like Update, I realized that I need to use Create first and then update like this:

func TestUpdateTODOTasks(t *testing.T) {
	ts := NewDB()
	var elem = &Element{fieldA : "A" , fieldB : "B"}
	ts.Create(elem)
	if err != nil {
		t.Errorf("=> failed to create: %v", err.Error())
	}
	var updated_elem = &Element{fieldA : "A-updated" , fieldB : "B"}

	err = ts.Update(updated_elem )

	if err != nil {
		t.Errorf("=> failed to update: %v", err.Error())
	}

}

答案1

得分: 0

你可以依赖于实现细节,初始化你的底层映射,确保你的存储确实在底层使用了映射。

一般来说,你可以从你描述的测试中获益。通过使用定义的API来初始化被测试的存储,可以使你的测试更接近客户端使用你的代码的方式。没有必要人为地修改底层状态。我见过很多这样的测试,它们通常很难维护且不稳定。

不要过于依赖于单元测试必须精确检查一个函数的事实。事实上,它们更多地是关于测试软件的整体、独立、小部分,所以不一定是一个单一的函数。

英文:

You can initialize your underlying map relying on the implementation detail that your storage indeed uses the map under the hood.

In general, you can really benefit from the tests you described. So seed the tested storage by using the defined API. It brings your tests closer to the way a client uses your code. There is no need to artificially modify the underlying state. I’ve seen a lot of tests doing so and they usually become hard to maintain and flaky.

Don’t be so attach to the fact than unit tests have to check exactly one function. In fact, they’re more about testing integral, independent, small parts of the software so it doesn’t have to be a single function at all.

huangapple
  • 本文由 发表于 2023年2月27日 06:21:54
  • 转载请务必保留本文链接:https://go.coder-hub.com/75575356.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定