英文:
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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论