Unit testing 单元测试XDB查询的最简单方法应该是什么
我有一个只对XDB进行查询(读/写)的服务 我想对这个进行单元测试,但我不知道怎么做,我读过很多关于嘲笑的图图。很多公司都在处理类似这样的组件。但由于我正在使用XDB,所以无法使用它 我还发现我尝试过使用的其他组件过于复杂 我认为要做的是创建一个存储库层,这个接口应该实现我运行/测试所需的所有方法,并通过依赖项注入传递具体的类 我想这可能行得通,但这是最简单的方法吗 我想到处都有存储库,即使是对于小型服务,仅仅是为了让它们可以测试,似乎设计过度了Unit testing 单元测试XDB查询的最简单方法应该是什么,unit-testing,go,mocking,Unit Testing,Go,Mocking,我有一个只对XDB进行查询(读/写)的服务 我想对这个进行单元测试,但我不知道怎么做,我读过很多关于嘲笑的图图。很多公司都在处理类似这样的组件。但由于我正在使用XDB,所以无法使用它 我还发现我尝试过使用的其他组件过于复杂 我认为要做的是创建一个存储库层,这个接口应该实现我运行/测试所需的所有方法,并通过依赖项注入传递具体的类 我想这可能行得通,但这是最简单的方法吗 我想到处都有存储库,即使是对于小型服务,仅仅是为了让它们可以测试,似乎设计过度了 如果需要的话,我可以给你一些代码,但我认为我的问
如果需要的话,我可以给你一些代码,但我认为我的问题更多的是理论性的,而不是实践性的。这是模拟自定义DB进行单元测试的最简单方法。根据其定义,如果您使用外部资源测试集成,我们谈论的是集成测试,而不是单元测试。所以我们有两个问题要解决 单元测试 您通常要做的是有一个数据访问层,它接受接口,而接口又很容易模拟,您可以对应用程序逻辑进行单元测试
package main
import (
"errors"
"fmt"
)
var (
values = map[string]string{"foo": "bar", "bar": "baz"}
Expected = errors.New("Expected error")
)
type Getter interface {
Get(name string) (string, error)
}
// ErrorGetter implements Getter and always returns an error to test the error handling code of the caller.
// ofc, you could (and prolly should) use some mocking here in order to be able to test various other cases
type ErrorGetter struct{}
func (e ErrorGetter) Get(name string) (string, error) {
return "", Expected
}
// MapGetter implements Getter and uses a map as its datasource.
// Here you can see that you actually get an advantage: you decouple your logic from the data source,
// making refactoring (and debugging) **much** easier WTSHTF.
type MapGetter struct {
data map[string]string
}
func (m MapGetter) Get(name string) (string, error) {
if v, ok := m.data[name]; ok {
return v, nil
}
return "", fmt.Errorf("No value found for %s", name)
}
type retriever struct {
g Getter
}
func (r retriever) retrieve(name string) (string, error) {
return r.g.Get(name)
}
func main() {
// Assume this is test code. No tests possible on playground ;)
bad := retriever{g: ErrorGetter{}}
s, err := bad.retrieve("baz")
if s != "" || err == nil {
panic("Something went seriously wrong")
}
// Needs to fail as well, as "baz" is not in values
good := retriever{g: MapGetter{values}}
s, err = good.retrieve("baz")
if s != "" || err == nil {
panic("Something went seriously wrong")
}
s, err = good.retrieve("foo")
if s != "bar" || err != nil {
panic("Something went seriously wrong")
}
}
在上面的例子中,我实际上必须实现两个getter来覆盖所有的测试用例,因为我不能使用模拟库,但您可以理解
至于过度工程:简单明了,不,这不是过度工程。这就是我个人所说的正当工艺。从长远来看,适应它是值得的。也许不是在这个项目中,而是在未来的一个项目中
集成测试
狡猾的。我倾向于在提交查询之前确保查询是正确的;)
在极少数情况下,我真的想在CI中验证我的查询,例如,我通常创建一个Makefile,然后生成一个docker(-compose),它提供我想要集成的内容,然后运行测试。要扩展@Markus W Mahlberg answer: 如果目标是验证查询是否有效并实际针对流入执行,则没有针对流入执行这些查询的快捷方式。这些通常被认为是“集成”测试。我发现使用docker compose,这些测试可以与单元测试一样可靠,并且速度足够快,可以集成到CI中。在CI中执行测试使本地工程师能够轻松地运行这些测试,以验证其查询更改 我想,到处都有存储库,即使是针对小型服务,只是为了让它们可以测试,这似乎是过度设计了。 我发现这是一个相当两极分化的讨论。并为支持轻松隔离和执行代码的特定组件铺平了道路
我想对此进行单元测试,但不确定如何进行, 我认为这是非常微妙的,IMO单元测试查询提供了负值。价值来自使用存储库接口,允许您的单元测试显式地配置您将从inflow接收的响应,以便充分使用您的应用程序代码。这不提供有关流入的反馈,这就是为什么集成测试对于验证应用程序是否可以有效地配置、连接和查询流入非常重要。此验证在部署应用程序时隐式发生,此时,就反馈而言,它比在本地和使用集成测试的CI中进行验证要昂贵得多
我创建了一个图表,试图说明这些差异:
使用repository进行的单元测试主要集中在您的应用程序代码上,对于与流入有关的任何事情都没有提供多少反馈/价值。集成测试对于验证您的客户机很有用(可能会扩展到您的应用程序,具体取决于测试执行的位置,但我更喜欢将其绑定到客户机,因为您已经获得了来自接口和调用的静态反馈)。最后,正如@Markus指出的,从集成测试到e2e测试的步骤非常小,并且允许您进行测试 我会选择界面。我不认为它们设计过度,因为它们为代码库添加了一个很好的抽象级别。IMO这一点也不过分:)我认为,在这里,我甚至会说,实际上集成测试应该针对从生产中克隆的登台环境进行。到那时,端到端测试只是一个小步骤,“仅”进行集成测试的目的和有效性变得相当可疑。好吧,对于synthetise来说,模拟DB进行单元测试并不会给我的测试带来太大的价值,所以我不必麻烦,相反,我应该进行集成测试,但你认为添加存储库层会增加价值,即使在小项目中,也应该进行编码,即使不需要模拟。这是正确的吗?