我目前正在考虑在 Go 中为我的服务创建一些单元测试,以及在该功能之上构建的其他功能,我想知道在 Go 中进行单元测试的最佳方法是什么?我的代码如下所示:
type BBPeripheral struct {
client *http.Client
endpoint string
}
type BBQuery struct {
Name string `json:"name"`
}
type BBResponse struct {
Brand string `json:"brand"`
Model string `json:"model"`
...
}
type Peripheral struct {
Brand string
Model string
...
}
type Service interface {
Get(name string) (*Peripheral, error)
}
func NewBBPeripheral(config *peripheralConfig) (*BBPeripheral, error) {
transport, err := setTransport(config)
if err != nil {
return nil, err
}
BB := &BBPeripheral{
client: &http.Client{Transport: transport},
endpoint: config.Endpoint[0],
}
return BB, nil
}
func (this *BBPeripheral) Get(name string) (*Peripheral, error) {
data, err := json.Marshal(BBQuery{Name: name})
if err != nil {
return nil, fmt.Errorf("BBPeripheral.Get Marshal: %s", err)
}
resp, err := this.client.Post(this.endpoint, "application/json", bytes.NewBuffer(data))
if resp != nil {
defer resp.Body.Close()
}
if err != nil {
return nil, err
}
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf(resp.StatusCode)
}
var BBResponse BBResponse
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
err = json.Unmarshal(body, &BBResponse)
if err != nil {
return nil, err
}
peripheral := &Peripheral{}
peripheral.Model = BBResponse.Model
if peripheral.Model == "" {
peripheral.Model = NA
}
peripheral.Brand = BBResponse.Brand
if peripheral.Brand == "" {
peripheral.Brand = NA
}
return peripheral, nil
}
测试此代码和使用这些函数启动单独的 goroutine 以充当服务器的代码的最有效方法是使用 http.httptest 包还是其他?那是我第一次尝试编写测试,然后我真的不知道该怎么做。
最佳答案
这真的完全取决于。 Go 提供了在每个级别测试应用程序所需的几乎所有工具。
单元测试
设计很重要,因为动态提供模拟/ stub 对象的技巧并不多。您可以覆盖测试变量,但它会解决各种清理问题。我会专注于 IO 自由单元测试,以检查您的特定逻辑是否有效。
例如,您可以通过使 client
成为一个接口(interface)来测试 BBPeripheral.Get
方法,在实例化期间需要它,并提供一个用于测试的 stub 。
func Test_BBPeripheral_Get_Success(*testing.T) {
bb := BBPeripheral{client: &StubSuccessClient, ...}
p, err := bb.Get(...)
if err != nil {
t.Fail()
}
}
然后您可以创建一个 stub 错误客户端,在 Get
方法中执行错误处理:
func Test_BBPeripheral_Get_Success(*testing.T) {
bb := BBPeripheral{client: &StubErrClient, ...}
_, err := bb.Get(...)
if err == nil {
t.Fail()
}
}
组件/集成测试
这些测试有助于锻炼您的包裹中的每个单独单元是否可以协同工作。由于您的代码通过 http 进行通信,因此 Go 提供了可以使用的 httptest
包。
为此,测试可以创建一个带有注册处理程序的 httptest 服务器,以提供 this.endpoint
期望的响应。然后,您可以通过请求 NewBBPeripheral
并传入与 Server.URL
对应的 this.endpoint
来使用其公共(public)接口(interface)来运行您的代码。属性(property)。
这允许您模拟您的代码与真实服务器的对话。
Go 例行测试
Go 使编写并发代码变得如此容易,并使测试它变得同样容易。测试生成一个执行 NewBBPeripheral
的 go 例程的顶级代码可能看起来非常像上面的测试。除了启动测试服务器之外,您的测试还必须等待您的异步代码完成。如果您没有服务范围内的方法来取消/关闭/发出完成信号,那么可能需要使用 go 例程对其进行测试。
RaceCondition/负载测试
使用 go 的内置基准测试结合 -race
标志,您可以轻松地运行您的代码,并利用您上面编写的测试针对竞争条件对其进行分析。
请记住一件事,如果您的应用程序的实现仍在不断变化,编写单元测试可能会花费大量时间。创建几个测试代码的公共(public)接口(interface),应该允许您轻松验证您的应用程序是否正常工作,同时允许更改实现。
关于unit-testing - golang 中的单元测试,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40681161/