广告
返回顶部
首页 > 资讯 > 后端开发 > GO >Golang实现单元测试中的接口层
  • 929
分享到

Golang实现单元测试中的接口层

Golang单元测试 接口层Golang单元测试Golang接口层 2023-03-11 11:03:15 929人浏览 八月长安
摘要

目录环境代码base case单元测试工具方法单元测试问题优化点总结上次我们已经搞定了逻辑层的单元测试,这次我们来康康接口层的单元测试。接口层主要负责的就是请求的处理,最常见的就是

上次我们已经搞定了逻辑层的单元测试,这次我们来康康接口层的单元测试。接口层主要负责的就是请求的处理,最常见的就是 Http 请求的处理。

但针对 接口层 的单元测试其实是可以五花八门的。它并不像逻辑层和数据层一样的通用,对于它的测试往往有很多路可以走。

由于使用的 HTTP 框架不同,单元测试的实现方式则不同。 既可以通过程序来模拟 HTTP 请求,也可以通过真实的 HTTP 请求来测试,通过借助外部的一些测试工具来实现。

所以本文只能给出一种思路,具体的实现方式还是要根据实际的框架来实现。

环境

本文以常用的 gin 框架为例,使用一种个人比较喜欢也非常简单的方式来实现单元测试。特点主要有:

  • 不需要启动路由服务
  • 复用已有的项目内的请求结构

代码

由于之前已经贴过,所以 service 层的 代码这里就不赘述了

base case

package controller

import (
    "context"

    "GitHub.com/gin-Gonic/gin"
    "go-demo/m/unit-test/entity"
)

//go:generate mockgen -source=./user.go -destination=../mock/user_service_mock.go -package=mock
type UserService interface {
    AddUser(ctx context.Context, username string) (err error)
    GetUser(ctx context.Context, userID int) (user *entity.User, err error)
}

type AddUserRequest struct {
    Username string `JSON:"username" binding:"required"`
}

type GetUserRequest struct {
    UserID int `fORM:"user_id" binding:"required"`
}

type GetUserResponse struct {
    Username string `json:"username"`
}

type UserController struct {
    UserService UserService
}

func NewUserController(userService UserService) *UserController {
    return &UserController{UserService: userService}
}

func (uc *UserController) AddUser(ctx *gin.Context) {
    req := &AddUserRequest{}
    if err := ctx.BindJSON(req); err != nil {
        return
    }
    if err := uc.UserService.AddUser(ctx, req.Username); err != nil {
        ctx.JSON(400, gin.H{"error": err.Error()})
        return
    }
    ctx.JSON(200, gin.H{"message": "success"})
}

func (uc *UserController) GetUser(ctx *gin.Context) {
    req := &GetUserRequest{}
    if err := ctx.BindQuery(req); err != nil {
        return
    }
    user, err := uc.UserService.GetUser(ctx, req.UserID)
    if err != nil {
        ctx.JSON(400, gin.H{"error": err.Error()})
        return
    }
    ctx.JSON(200, &GetUserResponse{Username: user.Username})
}
  • 既然之前我们 service 的单元测试已经通过,这次我们就需要 mock 的是 service 层的接口 mockgen -source=./user.go -destination=../mock/user_service_mock.go -package=mock
  • 这里我将请求和返回的结构 如:GetUserRequest、GetUserResponse 放在了这里仅仅是为了方便展示代码

单元测试

基础代码非常简单,就是我们常见的,最重要的让我们来看看单元测试应该怎么写

工具方法

在编写实际单元测试之前,我们需要一些工具方法来帮助我们构建一些请求。

func createGetReqCtx(req interface{}, handlerFunc gin.HandlerFunc) (isSuccess bool, resp string) {
    w := httptest.NewRecorder()
    c, _ := gin.CreateTestContext(w)
    encode := structToURLValues(req).Encode()
    c.Request, _ = http.NewRequest("GET", "/?"+encode, nil)
    handlerFunc(c)
    return w.Code == http.StatusOK, w.Body.String()
}

func createPostReqCtx(req interface{}, handlerFunc gin.HandlerFunc) (isSuccess bool, resp string) {
    responseRecorder := httptest.NewRecorder()
    ctx, _ := gin.CreateTestContext(responseRecorder)
    body, _ := json.Marshal(req)
    ctx.Request, _ = http.NewRequest("POST", "/", bytes.NewBuffer(body))
    ctx.Request.Header.Set("Content-Type", "application/json")

    handlerFunc(ctx)
    return responseRecorder.Code == http.StatusOK, responseRecorder.Body.String()
}

// 将结构体转换为 URL 参数
func structToURLValues(s interface{}) url.Values {
    v := reflect.ValueOf(s)
    if v.Kind() == reflect.Ptr {
        v = v.Elem()
    }
    t := v.Type()

    values := url.Values{}
    for i := 0; i < t.NumField(); i++ {
        field := t.Field(i)
        tag := field.Tag.Get("form")
        if tag == "" {
            continue
        }

        value := v.Field(i).Interface()
        values.Set(tag, valueToString(value))
    }

    return values
}

// 由于 get 请求常常参数并不会特别复杂,通常的几种类型就应该可以包括,有需要可以继续添加
func valueToString(v interface{}) string {
    switch v := v.(type) {
    case int:
        return strconv.Itoa(v)
    case string:
        return v
    default:
        return ""
    }
}

既然我们不想启动路由,其实最关键的问题就在如何构建一个 gin.Context 来模拟正常的请求。

  • 通过 gin.CreateTestContext 创建一个我们需要模拟的 context
  • 通过 http.NewRequest 来创建我们需要的请求结构

单元测试

有了我们的工具方法,那么编写单元测试的时候就非常方便了,mock 方法和之前类似,剩下要调用对应的方法就可以了。并且这里可以复用我们已经在原有程序中使用的 请求结构 如 GetUserRequest 这样就可以不需要重新劳动了。

package controller

import (
    "fmt"
    "testing"

    "github.com/golang/mock/gomock"
    "github.com/stretchr/testify/assert"
    "go-demo/m/unit-test/entity"
    "go-demo/m/unit-test/mock"
)

func TestUserController_AddUser(t *testing.T) {
    ctl := gomock.NewController(t)
    defer ctl.Finish()

    req := &AddUserRequest{Username: "LinkinStar"}
    mockUserService := mock.NewMockUserService(ctl)
    mockUserService.EXPECT().AddUser(gomock.Any(), gomock.Any()).Return(nil)

    userController := NewUserController(mockUserService)

    success, resp := createPostReqCtx(req, userController.AddUser)
    assert.True(t, success)
    fmt.Println(resp)
}

func TestUserController_GetUser(t *testing.T) {
    ctl := gomock.NewController(t)
    defer ctl.Finish()

    req := &GetUserRequest{UserID: 1}
    user := &entity.User{Username: "LinkinStar"}
    mockUserService := mock.NewMockUserService(ctl)
    mockUserService.EXPECT().GetUser(gomock.Any(), gomock.Any()).Return(user, nil)

    userController := NewUserController(mockUserService)

    success, resp := createGetReqCtx(req, userController.GetUser)
    assert.True(t, success)
    fmt.Println(resp)
}

可以看到测试方法如出一辙,再详细的话只需要对请求的返回值做解析然后进行断言即可。

问题

当然以上述方式来实现单元测试的话,是会遗漏一些问题,毕竟偷懒是要有代价的。

  • 路由路径的问题:可以看到上述的单元测试中并没有注册对应的 url 地址,那么实际中可能会由于代码路由的书写错误而导致 404 的情况
  • 请求结构字段错误:由于我们复用了原有代码中的请求结构,即使单词拼写错误依然能成功,因为两边都一样错,所以即使字段名称与接口文档不一致也无法发现。

针对这两个问题,我觉得可以由更加上层的测试来保证,由于这里仅仅是单元测试,我觉得这些代价还是可以接受的。并且,如果是使用 swagger 生成文档的情况下,也能保证文档和代码的统一性。但在此还是要出来提个醒,毕竟实际问题我还是遇到过的。

优化点

当然,这里的举例还是过于简单,实际中的请求往往会比较复杂。

  • 实际场景往往一些请求需要鉴权,这个可以在根据实际你的鉴权方式在前面添加中间件统一来处理登录就可以
  • 其他类型的请求也是类似的如 PUT、DELETE 等
  • 当前只是简单的处理了正常的 200 HTTP Code 还会出现其他异常的情况也需要按实际接口进行处理

总结

通常从现象来说,这一层的测试往往发现的问题比较少,是由于这一层的逻辑少,测试下来最常见的问题往往就是字段名称和限制条件不满足需求。所以其实从性价比的角度来说,单独对这层拿出来测试往往比较低,故实际中见到的比较少。

不过话又说回来了,本文的目的不仅仅是为了让你了解到可以这样写单元测试,其中使用的方法往往还能再某些时候让你复用 handler 的方法来保证系统的一致性。

到此这篇关于Golang实现单元测试中的接口层的文章就介绍到这了,更多相关Golang单元测试内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

您可能感兴趣的文档:

--结束END--

本文标题: Golang实现单元测试中的接口层

本文链接: https://www.lsjlt.com/news/199339.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • Golang实现单元测试中的接口层
    目录环境代码base case单元测试工具方法单元测试问题优化点总结上次我们已经搞定了逻辑层的单元测试,这次我们来康康接口层的单元测试。接口层主要负责的就是请求的处理,最常见的就是 ...
    99+
    2023-03-11
    Golang单元测试 接口层 Golang单元测试 Golang接口层
  • Golang如何实现单元测试中的接口层
    这篇文章主要介绍“Golang如何实现单元测试中的接口层”,在日常操作中,相信很多人在Golang如何实现单元测试中的接口层问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Golang如何实现单元测试中的接口层...
    99+
    2023-07-05
  • Golang实现单元测试中的逻辑层
    目录准备工作基本 case 代码生成 mock 接口编写单元测试优化mockgen总结前面我们完成了最麻烦的数据层的单元测试,今天我们来看看单元测试中最容易做的一层,数据逻辑层,也就...
    99+
    2023-03-10
    Golang单元测试 逻辑层 Golang单元测试 Go 单元测试
  • Golang如何实现单元测试中的逻辑层
    本篇内容介绍了“Golang如何实现单元测试中的逻辑层”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!准备工作安装 go insta...
    99+
    2023-07-05
  • Golang单元测试中的技巧分享
    目录打桩测试代码注意事项压测代码使用测试覆盖率表格驱动测试打桩测试 当我们在编写单元测试的时候,有时我们非常想 mock 掉其中一个方法,但是这个方法又没有接口去定义和实现(无法用&...
    99+
    2023-03-13
    Golang单元测试技巧 Golang单元测试 Go 单元测试
  • python---简单的接口测试实例
    我们可以用Jmeter做接口测试,但是呢个人觉得那个有点局限性,用python就灵活很多,   可以按自己的思路来构建比较灵活,下面给大家介绍一个简单的接口测试实例。   一、我们的思路如下:   首先我们要弄清楚我们的整个思路: 1.先...
    99+
    2023-01-31
    实例 接口 简单
  • Golang 端口复用测试的实现
    先给出结论: 同一个进程,使用一个端口,然后连接关闭,大约需要30s后才可再次使用这个端口。 测试 首先使用端口9001连接服务端,发送数据,然后关闭连接,接着再次使用端口9001...
    99+
    2022-11-11
  • Golang单元测试中的技巧是什么
    这篇文章主要讲解了“Golang单元测试中的技巧是什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Golang单元测试中的技巧是什么”吧!打桩测试当我们在编写单元测试的时候,有时我们非常想...
    99+
    2023-07-05
  • SpringBoot+TestNG单元测试的实现
    目录背景接口测试用例,针对入参进行设计:言归正传!背景 由于开发任务进度紧张,接口及基础数据提供不全,即使设计全面的接口测试用例也无法全面有效的进行覆盖测试;且又因为单接口测试用例...
    99+
    2022-11-12
  • GO中的单元测试怎么实现
    这篇“GO中的单元测试怎么实现”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“GO中的单元测试怎么实现”文章吧。  ...
    99+
    2023-07-04
  • 在Android Studio中实现单元测试
      我们写了一个类,我们只希望测试下其算法是否正确,没必要真正运行一个完整的 APP,做单元测试即可。   在 Android Studio 中,我们在 java 文件夹...
    99+
    2022-06-06
    Android Studio studio 单元 单元测试 测试 Android
  • Node.js中怎么实现单元测试
    Node.js中怎么实现单元测试,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。为啥需要单元测试?所谓单元测试,就是对某个函数或者API进行正确性验证。来看个简单的例子add1....
    99+
    2023-06-17
  • Java中怎么实现单元测试与集成测试
    Java中怎么实现单元测试与集成测试,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。Maven测试代码结构的组织我们知道在Maven工程结构中“src/test”目录是专门用...
    99+
    2023-06-16
  • Golang端口复用测试的实现方法
    小编给大家分享一下Golang端口复用测试的实现方法,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!先给出结论:同一个进程,使用一个端口,然后连接关闭,大约需要30...
    99+
    2023-06-14
  • Go语言单元测试怎么实现服务请求和接口返回
    这篇“Go语言单元测试怎么实现服务请求和接口返回”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“Go语言单元测试怎么实现服务请...
    99+
    2023-07-02
  • python中怎么实现unittest单元测试
    python中怎么实现unittest单元测试,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。说明导入unittest模块。导入被测对象。创建测试类unittest.TestCa...
    99+
    2023-06-20
  • golang 对私有函数进行单元测试的实例
    在待测试的私有函数所在的包内,新建一个xx_test.go文件 书写方式如下: import ( "github.com/stretchr/testify/assert" "...
    99+
    2022-11-12
  • SpringBoot+TestNG单元测试的实现方法
    这篇文章主要讲解了“SpringBoot+TestNG单元测试的实现方法”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“SpringBoot+TestNG单元测试的实现方法”吧!目录背景接口测...
    99+
    2023-06-20
  • SpringBoot+JUnit5+MockMvc+Mockito单元测试的实现
    目录版本 项目结构 EchoServiceImpl EchoControllerNoMockitoTest EchoControllerMockTest 今天聊聊如何在 Spring...
    99+
    2022-11-12
  • 怎么实现Python的add5()单元测试
    本篇内容介绍了“怎么实现Python的add5()单元测试”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!TestAdd5类由unittest...
    99+
    2023-06-17
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作