{"id":15074386,"url":"https://github.com/seth-shi/go-zero-testing-example","last_synced_at":"2025-04-10T18:45:08.391Z","repository":{"id":254830852,"uuid":"847647499","full_name":"seth-shi/go-zero-testing-example","owner":"seth-shi","description":"go-zero unit testing and integration testing","archived":false,"fork":false,"pushed_at":"2024-09-20T10:41:16.000Z","size":102,"stargazers_count":4,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-24T16:35:22.681Z","etag":null,"topics":["go","go-zero","golang","grpc","grpc-testing","unit-testing"],"latest_commit_sha":null,"homepage":"https://www.shiguopeng.cn/posts/2024082617/","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/seth-shi.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-08-26T09:18:00.000Z","updated_at":"2025-03-19T13:25:17.000Z","dependencies_parsed_at":"2024-08-26T12:53:43.753Z","dependency_job_id":"8af9b263-40b6-44be-bb4b-fbb0f0c592ac","html_url":"https://github.com/seth-shi/go-zero-testing-example","commit_stats":{"total_commits":15,"total_committers":2,"mean_commits":7.5,"dds":0.2666666666666667,"last_synced_commit":"59d29c5851269ae6f300bb83eeeeef053cdd47f3"},"previous_names":["seth-shi/go-zero-testing-example"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/seth-shi%2Fgo-zero-testing-example","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/seth-shi%2Fgo-zero-testing-example/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/seth-shi%2Fgo-zero-testing-example/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/seth-shi%2Fgo-zero-testing-example/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/seth-shi","download_url":"https://codeload.github.com/seth-shi/go-zero-testing-example/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248271925,"owners_count":21075800,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["go","go-zero","golang","grpc","grpc-testing","unit-testing"],"created_at":"2024-09-25T03:32:45.058Z","updated_at":"2025-04-10T18:45:08.373Z","avatar_url":"https://github.com/seth-shi.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"## 开始\n![Coverage](https://img.shields.io/badge/Coverage-100.0%25-brightgreen)\n\n#### 涉及测试 \u0026\u0026 方法\n\n* `export_test.go`使用此文件不用写测试, 正式代码不会包含, 可以定义数据\n* 销毁创建方法\n\n```go\nfunc setUp(testName string) func() {\n    fmt.Printf(\"\\tsetUp fixture for %s\\n\", testName)\n    return func() {\n        fmt.Printf(\"\\ttearDown fixture for %s\\n\", testName)\n    }\n}\n\nfunc TestFunc1(t *testing.T) {\n  defer setUp(t.Name())()\n  fmt.Printf(\"\\tExecute test: %s\\n\", t.Name())\n}\n\nfunc TestMain(m *testing.M) {\n  defer pkgSetUp(\"package demo_test\")()\n  m.Run()\n}\n```\n\n* 单元测试\n    * 业务的实现代码基本都是写单元测试, 比如在`go-zero`内部的`logic`\n    * 所有的依赖都使用`mock`, 比如数据库就使用[sql-mock](https://github.com/DATA-DOG/go-sqlmock), [redis-mock](https://github.com/go-redis/redismock), 其它依赖使用接口[testify-mock](https://github.com/stretchr/testify?tab=readme-ov-file#mock-package)\n    * 更多**Mock**方案可参考[[https://github.com/bouk/monkey?tab=readme-ov-file](https://github.com/bouk/monkey?tab=readme-ov-file)\n      ]([https://github.com/bouk/monkey?tab=readme-ov-file](https://github.com/bouk/monkey?tab=readme-ov-file)\n      )\n* 集成测试\n    * 有服务依赖的, 比如数据库依赖, 其它服务依赖. 会去启动一个别的服务\n    * 数据库依赖使用`go-mysql-server`, `redis`使用`mini-redis`(也可以启动一个真正的数据库来测试)\n\n#### 例子仓库地址\n* [https://github.com/seth-shi/go-zero-testing-example](https://github.com/seth-shi/go-zero-testing-example)\n* 服务的架构如下\n    * **id** 服务是雪花**id**服务, 零依赖\n    * **post** 服务依赖**雪花服务**, **数据库**,  **Redis**\n```shell\n├─app\n│  ├─id\n│  │  └─rpc\n│  │      ├─etc (配置文件)\n│  │      ├─id (grpc 代码生成)\n│  │      └─internal\n│  │          ├─config (配置定义)\n│  │          ├─logic (业务逻辑)\n│  │          ├─mock (单元测试数据)\n│  │          ├─server (go-zero 服务端生成)\n│  │          └─svc (服务依赖定义)\n│  └─post\n│      └─rpc\n│          ├─etc (配置文件)\n│          ├─internal\n│          │  ├─config (配置定义)\n│          │  ├─faker (集成测试数据)\n│          │  ├─logic (业务逻辑)\n│          │  ├─mock (单元测试数据)\n│          │  ├─model (gorm 生成)\n│          │  │  ├─do (数据库查询操作)\n│          │  │  └─entity (gorm gen 生成模型定义)\n│          │  ├─server (go-zero 服务端生成)\n│          │  └─svc (服务依赖定义)\n│          └─post (grpc 代码生成)\n└─pkg (公共代码)\n└─go.mod\n```\n\n## 单元测试\n\n#### id 服务\n\n```protobuf\nsyntax = \"proto3\";\n\npackage id;\noption go_package=\"./id\";\n\nmessage IdRequest {\n}\n\nmessage IdResponse {\n  uint64 id = 1;\n  uint64 node = 2;\n}\n\nservice Id {\n  rpc Get(IdRequest) returns(IdResponse);\n}\n```\n\n* 这个服务使用索尼的雪花算法生成**id** ([https://github.com/sony/sonyflake/issues](https://github.com/sony/sonyflake/issues)), 代码非常简单, 我们这里直接跳过说明\n\n#### post 服务\n\n```protobuf\nsyntax = \"proto3\";\n\npackage post;\noption go_package=\"./post\";\n\nmessage PostRequest {\n  uint64  id = 1;\n}\n\nmessage PostResponse {\n  uint64 id = 1;\n  string title = 2;\n  string content = 3;\n  uint64 createdAt = 4;\n  uint64 viewCount = 5;\n}\n\nservice Post {\n  rpc Get(PostRequest) returns(PostResponse);\n}\n\n\n```\n\n#### 代码\n\n* 服务依赖\n\n```go\npackage svc\n\nimport (\n\t\"github.com/redis/go-redis/v9\"\n\t\"github.com/seth-shi/go-zero-testing-example/app/id/rpc/id\"\n\t\"github.com/seth-shi/go-zero-testing-example/app/post/rpc/internal/config\"\n\t\"github.com/seth-shi/go-zero-testing-example/app/post/rpc/internal/model/do\"\n\t\"github.com/seth-shi/go-zero-testing-example/app/post/rpc/internal/model/entity\"\n\t\"github.com/zeromicro/go-zero/core/logx\"\n\t\"github.com/zeromicro/go-zero/zrpc\"\n\t\"gorm.io/driver/mysql\"\n\t\"gorm.io/gorm\"\n)\n\ntype ServiceContext struct {\n\tConfig config.Config\n\tRedis  *redis.Client\n\tIdRpc  id.IdClient\n\n\tQuery *do.Query\n}\n\nfunc NewServiceContext(c config.Config) *ServiceContext {\n\n\tconn, err := gorm.Open(mysql.Open(c.DataSource))\n\tlogx.Must(err)\n\n\tidClient := id.NewIdClient(zrpc.MustNewClient(c.IdRpc).Conn())\n\tentity.SetIdGenerator(idClient)\n\n\trdb := redis.NewClient(\n\t\t\u0026redis.Options{\n\t\t\tAddr:     c.RedisConf.Host,\n\t\t\tPassword: c.RedisConf.Pass,\n\t\t\tDB:       0,\n\t\t},\n\t)\n\n\treturn \u0026ServiceContext{\n\t\tConfig: c,\n\t\tRedis:  rdb,\n\t\tIdRpc:  idClient,\n\t\tQuery:  do.Use(conn),\n\t}\n}\n```\n\n* **!!!业务逻辑**\n\n```go\npackage logic\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/samber/lo\"\n\t\"github.com/seth-shi/go-zero-testing-example/app/post/rpc/internal/svc\"\n\t\"github.com/seth-shi/go-zero-testing-example/app/post/rpc/post\"\n\n\t\"github.com/zeromicro/go-zero/core/logx\"\n)\n\ntype GetLogic struct {\n\tctx    context.Context\n\tsvcCtx *svc.ServiceContext\n\tlogx.Logger\n}\n\nfunc NewGetLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetLogic {\n\treturn \u0026GetLogic{\n\t\tctx:    ctx,\n\t\tsvcCtx: svcCtx,\n\t\tLogger: logx.WithContext(ctx),\n\t}\n}\n\nfunc (l *GetLogic) Get(in *post.PostRequest) (*post.PostResponse, error) {\n\n\t// 数据库查询数据\n\tp, err := l.\n\t\tsvcCtx.\n\t\tQuery.\n\t\tPost.\n\t\tWithContext(l.ctx).\n\t\tWhere(l.svcCtx.Query.Post.ID.Eq(in.GetId())).\n\t\tFirst()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// redis + 1 浏览量\n\tredisKey := fmt.Sprintf(\"post:%d\", p.ID)\n\tval, err := l.svcCtx.Redis.Incr(l.ctx, redisKey).Result()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tresp := \u0026post.PostResponse{\n\t\tId:        p.ID,\n\t\tTitle:     lo.FromPtr(p.Title),\n\t\tContent:   lo.FromPtr(p.Content),\n\t\tCreatedAt: uint64(p.CreatedAt.Unix()),\n\t\tViewCount: uint64(val),\n\t}\n\treturn resp, nil\n}\n```\n\n#### 开始写单元测试\n\n* 业务逻辑单测\n\n```go\npackage logic\n\nimport (\n\t\"context\"\n\t\"database/sql/driver\"\n\t\"errors\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/DATA-DOG/go-sqlmock\"\n\t\"github.com/seth-shi/go-zero-testing-example/app/post/rpc/internal/config\"\n\t\"github.com/seth-shi/go-zero-testing-example/app/post/rpc/internal/mock\"\n\t\"github.com/seth-shi/go-zero-testing-example/app/post/rpc/internal/model/do\"\n\t\"github.com/seth-shi/go-zero-testing-example/app/post/rpc/internal/svc\"\n\t\"github.com/seth-shi/go-zero-testing-example/app/post/rpc/post\"\n\t\"github.com/stretchr/testify/require\"\n)\n\n// 注意, 此部分是单元测试, 不依赖任何外部依赖\n// 逻辑的实现尽量通过接口的方式去实现\n// 区别于服务根目录下的集成测试, 集成测试会启动服务包括依赖\nfunc TestGetLogic_Get(t *testing.T) {\n\n\tvar (\n\t\tmockVal = mock.GetValue()\n\t\tsvcCtx  = \u0026svc.ServiceContext{\n\t\t\tConfig: config.Config{},\n\t\t\tRedis:  mockVal.Redis,\n\t\t\tIdRpc:  mockVal.IdServer,\n\t\t\tQuery:  do.Use(mockVal.Database),\n\t\t}\n\t\terrNotFound      = errors.New(\"not found\")\n\t\terrRedisNotFound = errors.New(\"redis not found\")\n\t\tselectSql        = \"SELECT (.+) FROM `posts` WHERE `posts`.`id` = (.+)\"\n\t\tcolumns          = []string{\"id\", \"title\", \"content\", \"created_at\", \"updated_at\"}\n\t\trow              = []driver.Value{1, \"title\", \"content\", time.Now(), time.Now()}\n\t)\n\n\tlogic := NewGetLogic(context.Background(), svcCtx)\n\n\t// mock 数据库返回\n\tmockVal.\n\t\tDatabaseMock.\n\t\tExpectQuery(selectSql).\n\t\tWithArgs(1, 1).\n\t\tWillReturnRows(sqlmock.NewRows(columns).AddRow(row...))\n\tmockVal.RedisMock.ExpectIncr(\"post:1\").SetVal(1)\n\tresp, err := logic.Get(\u0026post.PostRequest{Id: 1})\n\trequire.NoError(t, err)\n\trequire.Equal(t, uint64(1), resp.GetId())\n\trequire.Equal(t, \"title\", resp.GetTitle())\n\n\t// redis 返回错误的场景\n\tmockVal.\n\t\tDatabaseMock.\n\t\tExpectQuery(selectSql).\n\t\tWithArgs(1, 1).\n\t\tWillReturnRows(sqlmock.NewRows(columns).AddRow(row...))\n\tmockVal.RedisMock.ExpectIncr(\"post:1\").SetErr(errRedisNotFound)\n\t_, err3 := logic.Get(\u0026post.PostRequest{Id: 1})\n\trequire.ErrorIs(t, err3, errRedisNotFound)\n\n\t// 数据库返回错误的场景\n\tmockVal.\n\t\tDatabaseMock.\n\t\tExpectQuery(selectSql).\n\t\tWithArgs(1, 1).\n\t\tWillReturnError(errNotFound)\n\t_, err2 := logic.Get(\u0026post.PostRequest{Id: 1})\n\trequire.ErrorIs(t, err2, errNotFound)\n}\n\n```\n\n* mock 包代码组装数据\n\n```go\npackage mock\n\nimport (\n\t\"context\"\n\t\"sync\"\n\n\t\"github.com/DATA-DOG/go-sqlmock\"\n\t\"github.com/go-redis/redismock/v9\"\n\t\"github.com/redis/go-redis/v9\"\n\t\"github.com/seth-shi/go-zero-testing-example/app/id/rpc/id\"\n\t\"github.com/stretchr/testify/mock\"\n\t\"github.com/zeromicro/go-zero/core/logx\"\n\t\"google.golang.org/grpc\"\n\t\"gorm.io/driver/mysql\"\n\t\"gorm.io/gorm\"\n)\n\ntype value struct {\n\tIdServer     *idMock\n\tDatabase     *gorm.DB\n\tDatabaseMock sqlmock.Sqlmock\n\tRedisMock    redismock.ClientMock\n\tRedis        *redis.Client\n\n\tcacheStore sync.Map\n}\n\nvar GetValue = sync.OnceValue(\n\tfunc() value {\n\n\t\tdb, dbMock := makeDatabase()\n\t\tredis, redisMock := redismock.NewClientMock()\n\n\t\treturn value{\n\t\t\tIdServer:     \u0026idMock{},\n\t\t\tDatabase:     db,\n\t\t\tDatabaseMock: dbMock,\n\t\t\tRedis:        redis,\n\t\t\tRedisMock:    redisMock,\n\t\t\tcacheStore:   sync.Map{},\n\t\t}\n\t},\n)\n\ntype idMock struct {\n\tmock.Mock\n}\n\nfunc (m *idMock) Get(ctx context.Context, in *id.IdRequest, opts ...grpc.CallOption) (*id.IdResponse, error) {\n\targs := m.Called()\n\tidResp := uint64(args.Int(0))\n\n\treturn \u0026id.IdResponse{\n\t\tId:   idResp,\n\t\tNode: idResp,\n\t}, args.Error(1)\n}\n\nfunc makeDatabase() (*gorm.DB, sqlmock.Sqlmock) {\n\n\tdb, dbMock, err := sqlmock.New()\n\tlogx.Must(err)\n\tdbMock.\n\t\tExpectQuery(\"SELECT VERSION()\").\n\t\tWillReturnRows(sqlmock.NewRows([]string{\"VERSION()\"}).AddRow(\"5.7\"))\n\n\tgormDB, err := gorm.Open(\n\t\tmysql.New(\n\t\t\tmysql.Config{\n\t\t\t\tConn: db,\n\t\t\t},\n\t\t), \u0026gorm.Config{},\n\t)\n\tlogx.Must(err)\n\n\treturn gormDB, dbMock\n}\n\n```\n\n* 至此, 我们就完成此业务代码的 **100%** 测试覆盖\n\n## 集成测试\n\n* 需要改造一下**main**方法\n\n```go\npackage main\n\nimport (\n\t\"flag\"\n\t\"fmt\"\n\n\t\"github.com/seth-shi/go-zero-testing-example/app/post/rpc/internal/config\"\n\t\"github.com/seth-shi/go-zero-testing-example/app/post/rpc/internal/server\"\n\t\"github.com/seth-shi/go-zero-testing-example/app/post/rpc/internal/svc\"\n\t\"github.com/seth-shi/go-zero-testing-example/app/post/rpc/post\"\n\t\"github.com/zeromicro/go-zero/core/conf\"\n\t\"github.com/zeromicro/go-zero/core/logx\"\n\t\"github.com/zeromicro/go-zero/core/service\"\n\t\"github.com/zeromicro/go-zero/zrpc\"\n\t\"google.golang.org/grpc\"\n\t\"google.golang.org/grpc/reflection\"\n)\n\nvar svcCtxGet = getCtxByConfigFile\n\nfunc getCtxByConfigFile() (*svc.ServiceContext, error) {\n\tflag.Parse()\n\tvar c config.Config\n\tif err := conf.Load(\"etc/post.yaml\", \u0026c); err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn svc.NewServiceContext(c), nil\n}\n\nfunc main() {\n\n\tctx, err := svcCtxGet()\n\tlogx.Must(err)\n\ts := zrpc.MustNewServer(\n\t\tctx.Config.RpcServerConf, func(grpcServer *grpc.Server) {\n\t\t\tpost.RegisterPostServer(grpcServer, server.NewPostServer(ctx))\n\n\t\t\tif ctx.Config.Mode == service.DevMode || ctx.Config.Mode == service.TestMode {\n\t\t\t\treflection.Register(grpcServer)\n\t\t\t}\n\t\t},\n\t)\n\tdefer s.Stop()\n\n\tfmt.Printf(\"Starting rpc server at %s...\\n\", ctx.Config.ListenOn)\n\ts.Start()\n}\n\n```\n\n* 集成测试方法\n\n```go\npackage main\n\nimport (\n\t\"context\"\n\t\"os\"\n\t\"testing\"\n\n\t\"github.com/redis/go-redis/v9\"\n\t\"github.com/samber/lo\"\n\t\"github.com/seth-shi/go-zero-testing-example/app/post/rpc/internal/config\"\n\t\"github.com/seth-shi/go-zero-testing-example/app/post/rpc/internal/faker\"\n\t\"github.com/seth-shi/go-zero-testing-example/app/post/rpc/internal/model/do\"\n\t\"github.com/seth-shi/go-zero-testing-example/app/post/rpc/internal/svc\"\n\t\"github.com/seth-shi/go-zero-testing-example/app/post/rpc/post\"\n\t\"github.com/stretchr/testify/require\"\n\t\"github.com/zeromicro/go-zero/zrpc\"\n)\n\nfunc TestMain(m *testing.M) {\n\n\t// 使用默认配置\n\tsvcCtxGet = func() (*svc.ServiceContext, error) {\n\n\t\tfakerVal := faker.GetValue()\n\t\treturn \u0026svc.ServiceContext{\n\t\t\tConfig: config.Config{\n\t\t\t\tRpcServerConf: zrpc.RpcServerConf{\n\t\t\t\t\tListenOn: fakerVal.RpcListen,\n\t\t\t\t},\n\t\t\t},\n\t\t\tRedis: redis.NewClient(\n\t\t\t\t\u0026redis.Options{\n\t\t\t\t\tAddr: fakerVal.RedisAddr,\n\t\t\t\t\tDB:   0,\n\t\t\t\t},\n\t\t\t),\n\t\t\tIdRpc: fakerVal.IdServer,\n\t\t\tQuery: do.Use(fakerVal.Gorm),\n\t\t}, nil\n\t}\n\n\tgo main()\n\n\t// 运行测试\n\tcode := m.Run()\n\tos.Exit(code)\n}\n\n// 集成测试\nfunc TestGet(t *testing.T) {\n\n\tvar (\n\t\tfakerVal  = faker.GetValue()\n\t\tpostModel = fakerVal.Models.PostModel\n\t)\n\tconn, err := zrpc.NewClient(\n\t\tzrpc.RpcClientConf{\n\t\t\tTarget:   fakerVal.RpcListen,\n\t\t\tNonBlock: false,\n\t\t},\n\t)\n\trequire.NoError(t, err)\n\tclient := post.NewPostClient(conn.Conn())\n\tresp, err := client.Get(context.Background(), \u0026post.PostRequest{Id: postModel.ID})\n\trequire.NoError(t, err)\n\trequire.NotZero(t, resp.GetId())\n\trequire.Equal(t, resp.GetId(), postModel.ID)\n\trequire.Equal(t, resp.Title, lo.FromPtr(postModel.Title))\n}\n\n```\n\n* 集成测试依赖服务\n\n```go\npackage faker\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"math/rand\"\n\t\"sync\"\n\n\t\"github.com/alicebob/miniredis/v2\"\n\t\"github.com/samber/lo\"\n\t\"github.com/seth-shi/go-zero-testing-example/app/id/rpc/id\"\n\t\"github.com/seth-shi/go-zero-testing-example/app/post/rpc/internal/model/entity\"\n\t\"github.com/seth-shi/go-zero-testing-example/pkg\"\n\t\"github.com/zeromicro/go-zero/core/logx\"\n\t\"google.golang.org/grpc\"\n\t\"gorm.io/driver/mysql\"\n\t\"gorm.io/gorm\"\n)\n\ntype value struct {\n\tIdServer    *idGenerator\n\tRedis       *miniredis.Miniredis\n\tModels      *fakerModels\n\tGorm        *gorm.DB\n\tRedisAddr   string\n\tRpcListen   string\n\tDatabaseDsn string\n}\n\nvar GetValue = sync.OnceValue(\n\tfunc() value {\n\n\t\tredis, redisAddr := pkg.FakerRedisServer()\n\t\tdsn := pkg.FakerDatabaseServer()\n\n\t\trpcPort, err := pkg.GetAvailablePort()\n\t\tlogx.Must(err)\n\n\t\tconn, err := gorm.Open(mysql.Open(dsn))\n\t\tlogx.Must(err)\n\n\t\tidGen := \u0026idGenerator{\n\t\t\tstartId: uint64(rand.Int() + 1),\n\t\t\tlocker:  \u0026sync.RWMutex{},\n\t\t}\n\t\treturn value{\n\t\t\tIdServer:    idGen,\n\t\t\tRedis:       redis,\n\t\t\tRedisAddr:   redisAddr,\n\t\t\tDatabaseDsn: dsn,\n\t\t\tModels:      makeDatabase(dsn, idGen),\n\t\t\tRpcListen:   fmt.Sprintf(\":%d\", rpcPort),\n\t\t\tGorm:        conn,\n\t\t}\n\t},\n)\n\ntype idGenerator struct {\n\tstartId uint64\n\tlocker  sync.Locker\n}\n\nfunc (m *idGenerator) Get(ctx context.Context, in *id.IdRequest, opts ...grpc.CallOption) (*id.IdResponse, error) {\n\n\tm.locker.Lock()\n\tdefer m.locker.Unlock()\n\n\tm.startId++\n\n\treturn \u0026id.IdResponse{\n\t\tId:   m.startId,\n\t\tNode: 1,\n\t}, nil\n}\n\n\ntype fakerModels struct {\n\tPostModel *entity.Post\n}\n\nfunc makeDatabase(dsn string, gen *idGenerator) *fakerModels {\n\n\tdb, err := gorm.Open(\n\t\tmysql.Open(dsn),\n\t)\n\tlogx.Must(err)\n\n\t// 创建表结构\n\tlogx.Must(db.Migrator().CreateTable(\u0026entity.Post{}))\n\n\t// 插入测试数据\n\tentity.SetIdGenerator(gen)\n\tpostModel := \u0026entity.Post{\n\t\tTitle:   lo.ToPtr(\"test\"),\n\t\tContent: lo.ToPtr(\"content\"),\n\t}\n\tlogx.Must(db.Create(postModel).Error)\n\tentity.SetIdGenerator(nil)\n\n\treturn \u0026fakerModels{PostModel: postModel}\n}\n```\n\n#### 集成测试依赖数据库\n\n```go\npackage pkg\n\nimport (\n\t\"fmt\"\n\t\"log\"\n\n\t\"github.com/alicebob/miniredis/v2\"\n\tsqle \"github.com/dolthub/go-mysql-server\"\n\t\"github.com/dolthub/go-mysql-server/memory\"\n\t\"github.com/dolthub/go-mysql-server/server\"\n\t\"github.com/zeromicro/go-zero/core/logx\"\n)\n\n// FakerDatabaseServer 测试环境可以使用容器化的 dsn/**\nfunc FakerDatabaseServer() string {\n\n\tvar (\n\t\tusername = \"root\"\n\t\tpassword = \"\"\n\t\thost     = \"localhost\"\n\t\tdbname   = \"test_db\"\n\t\tport     int\n\t\terr      error\n\t)\n\n\tdb := memory.NewDatabase(dbname)\n\tdb.BaseDatabase.EnablePrimaryKeyIndexes()\n\tprovider := memory.NewDBProvider(db)\n\tengine := sqle.NewDefault(provider)\n\tmysqlDb := engine.Analyzer.Catalog.MySQLDb\n\tmysqlDb.SetEnabled(true)\n\tmysqlDb.AddRootAccount()\n\n\tport, err = GetAvailablePort()\n\tlogx.Must(err)\n\n\tconfig := server.Config{\n\t\tProtocol: \"tcp\",\n\t\tAddress:  fmt.Sprintf(\"%s:%d\", host, port),\n\t}\n\ts, err := server.NewServer(\n\t\tconfig,\n\t\tengine,\n\t\tmemory.NewSessionBuilder(provider),\n\t\tnil,\n\t)\n\tlogx.Must(err)\n\tgo func() {\n\t\tlogx.Must(s.Start())\n\t}()\n\n\tdsn := fmt.Sprintf(\n\t\t\"%s:%s@tcp(%s:%d)/%s?charset=utf8mb4\u0026loc=Local\u0026parseTime=true\",\n\t\tusername,\n\t\tpassword,\n\t\thost,\n\t\tport,\n\t\tdbname,\n\t)\n\n\treturn dsn\n}\n\nfunc FakerRedisServer() (*miniredis.Miniredis, string) {\n\tm := miniredis.NewMiniRedis()\n\tif err := m.Start(); err != nil {\n\t\tlog.Fatalf(\"could not start miniredis: %s\", err)\n\t}\n\n\treturn m, m.Addr()\n}\n```\n\n* 至此, 就完成了集成测试的部分\n\n## End\n\n* 很多时候不可能写这么多测试代码, 这里就给一个例子, 后续继续完善\n* 完整代码 [https://github.com/seth-shi/go-zero-testing-example](https://github.com/seth-shi/go-zero-testing-example)","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fseth-shi%2Fgo-zero-testing-example","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fseth-shi%2Fgo-zero-testing-example","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fseth-shi%2Fgo-zero-testing-example/lists"}