概述
Golang是一门性能优异的静态范例语言,但因其奇快的编译速度,结合DevOps, 使得它也非常适合快速开发和迭代。
本文讲述怎样使用Golang, 进行持续集成与自动化测试和部署。主要使用了以下相关技术:
- dep: 进行包的依赖管理
- gin: 搭建 api 服务
- gorm:ORM, 数据CRUD
- mysql: 存储数据
- testfixtures: 测试夹具,在自动化测试时,自动向数据库填充用于测试的数据
- httpexpect: HTTP 测试包,用于API测试
- GoDotEnv: 情况变量处理
- go test: 使用test命令进行单元测试, 基准测试和 HTTP 测试
- GitLabCI: DevOps 工具
- golint: Golang 静态查抄工具
- migrate: 数据库迁移工具
- Docker: 使用 zacksleo/golang 镜像, 该镜像默认安装了 curl,git,build-base,dep 和 golint
- db2struct: 将数据库表结构一键天生为 struct(gorm的model)
- apig: 基于 gorm 和 gin 一键天生 CRUD API
开发流程
- 使用 apig 脚手架工具初始化项目结构和目录
- 使用 dep 安装相关依赖
- 使用 migrate 编写数据库迁移方法,并实行迁移创建数据表
- 使用 db2struct 天生 models
- 使用 apig 天生 crud 代码
- 使用 httpexpect 编写 api 测试代码,并通过 testfixtures 实现数据的自动填充
- 编写 GitLabCI 脚本进行持续集成
在上述过程中,如需连接数据库时,可通过 GoDotEnv 来实现情况变量的使用
相关CI脚本
- # golang-devops-and-auto-deploy
- image: zacksleo/golang
- stages:
- - test
- - build
- - deploy
- variables:
- GOPATH: /root
- before_script:
- - mkdir -p ~/src/github.com/zacksleo/projectname
- - cp -r . ~/src/github.com/zacksleo/projectname
- - cd ~/src/github.com/zacksleo/projectname
- lint:
- stage: test
- script:
- - golint -set_exit_status
- unit-tests:
- stage: test
- services:
- - mysql:5.6
- variables:
- MYSQL_ROOT_PASSWORD: root
- MYSQL_DATABASE: web
- MYSQL_USER: web
- MYSQL_PASSWORD: web
- script:
- - dep ensure
- - cp tests/.env .env
- - migrate -database "mysql://web:web@tcp(mysql:3306)/web" -path "./db/migrations/" up
- - go test -tags=unit_tests $(go list ./... | grep -v /vendor/ ./tests/api) -v -coverprofile .testCoverage.txt
- coverage: '/^coverage:\s(\d+(?:\.\d+)?%)/'
- integration-tests:
- stage: test
- services:
- - mysql:5.6
- variables:
- MYSQL_ROOT_PASSWORD: root
- MYSQL_DATABASE: web
- MYSQL_USER: web
- MYSQL_PASSWORD: web
- script:
- - dep ensure
- - cp tests/.env .env
- - migrate -database "mysql://web:web@tcp(mysql:3306)/web" -path "./db/migrations/" up
- - go test -tags=integration $(go list ./tests/... | grep -v /vendor/) -v
- build-bin:
- stage: test
- script:
- - dep ensure
- - env GOOS=linux GOARCH=386 go build -o $CI_PROJECT_DIR/debug
- artifacts:
- expire_in: 60 mins
- untracked: true
- name: "app"
- paths:
- - debug
- when: on_success
- build-image:
- image: docker
- stage: build
- dependencies:
- - build-bin
- script:
- - docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY
- - docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_TAG .
- - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_TAG
- - docker rmi $CI_REGISTRY_IMAGE:$CI_COMMIT_TAG
- only:
- - tags
- tags:
- - go
- deploy:
- image: zacksleo/node
- stage: deploy
- before_script:
- - eval $(ssh-agent -s)
- - echo "$SSH_PRIVATE_KEY" > ~/deploy.key
- - chmod 0600 ~/deploy.key
- - ssh-add ~/deploy.key
- - mkdir -p ~/.ssh
- - '[[ -f /.dockerenv ]] && echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config'
- script:
- - cd deploy/production
- - rsync -rtvhze ssh . root@$DEPLOY_SERVER:/data/gitlab/projectname --stats
- - ssh root@$DEPLOY_SERVER "docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY"
- - ssh root@$DEPLOY_SERVER "cd /data/gitlab/projectname && echo -e '\nTAG=$CI_COMMIT_TAG' >> .env && docker-compose pull app && docker-compose stop app && docker-compose rm -f app && docker-compose up -d app"
- only:
- - tags
复制代码 集成测试
- // +build integration
- package api
- import (
- "log"
- "os"
- "testing"
- "github.com/gin-gonic/gin"
- "github.com/jinzhu/gorm"
- "github.com/joho/godotenv"
- "gitlab.com/moguyun/api/debug/db"
- "gitlab.com/moguyun/api/debug/server"
- testfixtures "gopkg.in/testfixtures.v2"
- )
- var (
- fixtures *testfixtures.Context
- s *gin.Engine
- )
- // PrepareTestDatabase for test
- func PrepareTestDatabase(db *gorm.DB) {
- var fixtures *testfixtures.Context
- var err error
- testfixtures.SkipDatabaseNameCheck(true)
- fixtures, err = testfixtures.NewFolder(db.DB(), &testfixtures.MySQL{}, "../fixtures")
- if err != nil {
- log.Fatal(err)
- }
- if err = fixtures.Load(); err != nil {
- log.Fatal(err)
- }
- }
- // TestMain setup database
- func TestMain(m *testing.M) {
- // Open connection with the test database.
- godotenv.Load("../.env")
- database := db.Connect()
- s = server.Setup(database)
- PrepareTestDatabase(database)
- os.Exit(m.Run())
- }
- func TestToken(t *testing.T) {
- t.Run("CreateToken", TestCreateToken)
- }
- func TestCustomer(t *testing.T) {
- t.Run("GetCustomers", TestGetCustomers)
- t.Run("GetCustomer", TestGetCustomer)
- t.Run("CreateCustomer", TestCreateCustomer)
- t.Run("UpdateCustomer", TestUpdateCustomer)
- t.Run("DeleteCustomer", TestDeleteCustomer)
- }
复制代码 留意事项
在测试中,假如需要区分单元测试和集成测试,可以使用 build tags 实现,如在文件头部中添加 // +build integration, 运行测试使用 - go test -tags=integration $(go list ./tests/... | grep -v /vendor/) -v 可以只实行集成测试
参考文档
- Go Test
- How to write benchmarks in Go
- Go 性能调优之 —— 基准测试
- Go语言圣经(中文版)
- 使用tags区隔单元测试和集成测试
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
|