unit-testing - golang 中的单元测试

标签 unit-testing go

我目前正在考虑在 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/

相关文章:

java - 在测试上下文中传递 HTTP 调用

javascript - 如何使用 jest 模拟 $()

go - 为什么调用用户定义类型的用户定义 String() 会抛出 "not enough arguments in call to BitFlag.String"?

logging - Golang 微秒在日志包中

java - Scala/Gradle项目…哪里存储单元测试所需的超大文件?

javascript - 模拟文件中的 spy 功能

angularjs - Jasmine:为什么 beforeEach() 在嵌套描述中工作,而 beforeAll() 没有?

mongodb - mgo $unwind 聚合结果到未知元素种类 (0x2E)

go - 服务帐户流程-2条腿的Oauth Golang

ubuntu - ubuntu 17.10下Go安装tensorflow报错