golang 单元测试 命令行 日志打印 测试结果打印控制台
test.bat
@REM go test -timeout 30s -run ^TestMultiPong$ github.com/jergoo/go-grpc-tutorial/ping
@REM go test -timeout 30s -run ^TestPing$ github.com/jergoo/go-grpc-tutorial/ping
@REM go test -timeout 30s -run ^TestPingV1$ github.com/jergoo/go-grpc-tutorial/ping
▼▼ 添加-v (verbose) 添加到命令行参数中,控制台打印日志信息
go test -v -timeout 30s -run ^TestPingV2$ github.com/jergoo/go-grpc-tutorial/ping
go.mod
module github.com/jergoo/go-grpc-tutorial
go 1.19
require (
github.com/golang/protobuf v1.5.2
google.golang.org/grpc v1.51.0
google.golang.org/protobuf v1.28.1
)
require (
golang.org/x/net v0.2.0 // indirect
golang.org/x/sys v0.2.0 // indirect
golang.org/x/text v0.4.0 // indirect
google.golang.org/genproto v0.0.0-20221118155620-16455021b5e6 // indirect
)
go.sum
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
golang.org/x/net v0.2.0 h1:sZfSu1wtKLGlWI4ZZayP0ck9Y73K1ynO6gqzTdBVdPU=
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A=
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
google.golang.org/genproto v0.0.0-20221118155620-16455021b5e6 h1:a2S6M0+660BgMNl++4JPlcAO/CjkqYItDEZwkoDQK7c=
google.golang.org/genproto v0.0.0-20221118155620-16455021b5e6/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg=
google.golang.org/grpc v1.51.0 h1:E1eGv1FTqoLIdnBCZufiSHgKjlqG6fKFf6pPWtMTh8U=
google.golang.org/grpc v1.51.0/go.mod h1:wgNDFcnuBGmxLKI/qn4T+m5BtEBYXJPvibbUPsAIPww=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
ping\client.go
package main
import (
"context"
"io"
"log"
"time"
"google.golang.org/grpc"
pb "github.com/jergoo/go-grpc-tutorial/protos/ping" // 引入编译生成的包
)
// Ping 单次请求-响应模式
func Ping() {
conn, err := grpc.Dial("localhost:1234", grpc.WithInsecure())
if err != nil {
log.Fatal(err)
}
defer conn.Close()
// 实例化客户端并调用
client := pb.NewPingPongClient(conn)
res, err := client.Ping(context.Background(), &pb.PingRequest{Value: "ping"})
if err != nil {
log.Fatal(err)
}
log.Println(res.Value)
}
// MultiPong 服务端流模式
func MultiPong() {
conn, err := grpc.Dial("localhost:1234", grpc.WithInsecure())
if err != nil {
log.Fatal(err)
}
defer conn.Close()
// 实例化客户端并调用
client := pb.NewPingPongClient(conn)
// 获得对 stream 对象的引用
stream, err := client.MultiPong(context.Background(), &pb.PingRequest{Value: "ping"})
if err != nil {
log.Fatal(err)
}
// 循环接收数据流
for {
msg, err := stream.Recv()
if err != nil {
if err == io.EOF {
break
}
log.Fatal(err)
}
log.Println(msg.Value)
}
}
// MultiPing 客户端流模式
func MultiPing() {
conn, err := grpc.Dial("localhost:1234", grpc.WithInsecure())
if err != nil {
log.Fatal(err)
}
defer conn.Close()
// 实例化客户端并调用
client := pb.NewPingPongClient(conn)
stream, err := client.MultiPing(context.Background())
if err != nil {
log.Fatal(err)
}
// 发送数据
for i := 0; i < 5; i++ {
data := &pb.PingRequest{Value: "ping"}
err = stream.Send(data)
if err != nil {
log.Fatal(err)
}
}
// 发送结束并获取服务端响应
res, err := stream.CloseAndRecv()
if err != nil {
log.Fatal(err)
}
log.Println(res.Value)
}
// MultiPingPong 双向流模式
func MultiPingPong() {
conn, err := grpc.Dial("localhost:1234", grpc.WithInsecure())
if err != nil {
log.Fatal(err)
}
defer conn.Close()
// 实例化客户端并调用
client := pb.NewPingPongClient(conn)
stream, err := client.MultiPingPong(context.Background())
if err != nil {
log.Fatal(err)
}
// 在另一个goroutine中接收数据
c := make(chan struct{})
go func(stream pb.PingPong_MultiPingPongClient, c chan struct{}) {
defer func() {
c <- struct{}{}
}()
for {
msg, err := stream.Recv()
if err != nil {
if err == io.EOF {
break
}
log.Fatal(err)
}
log.Printf("recv:%s\n", msg.Value)
}
}(stream, c)
// 发送数据
for i := 0; i < 6; i++ {
data := &pb.PingRequest{Value: "ping"}
err = stream.Send(data)
if err != nil {
log.Fatal(err)
}
log.Printf("send:%s\n", data.Value)
time.Sleep(500 * time.Millisecond)
}
// 结束发送
stream.CloseSend()
// 等待接收完成
<-c
}
ping\client_test.go
package main
import (
"testing"
)
func TestPing(t *testing.T) {
tests := []struct {
name string
}{
{name: "ping"},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
Ping()
})
}
}
func TestMultiPong(t *testing.T) {
tests := []struct {
name string
}{
{name: "multi pong"},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
MultiPong()
})
}
}
func TestMultiPing(t *testing.T) {
tests := []struct {
name string
}{
{name: "multi ping"},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
MultiPing()
})
}
}
func TestMultiPingPong(t *testing.T) {
tests := []struct {
name string
}{
{name: "multi ping&pong"},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
MultiPingPong()
})
}
}
ping\clinet_v1_test.go
package main
import (
"io"
"log"
"net"
"sync"
"testing"
pb "github.com/jergoo/go-grpc-tutorial/protos/ping"
"google.golang.org/grpc"
)
// 定义一个全局变量用于保存服务端实例,方便后续关闭操作
var srv *grpc.Server
// 启动server的函数,和server.go中的启动逻辑类似,但做了简化调整以适应测试环境
func startServer() {
var err error
srv = grpc.NewServer()
pb.RegisterPingPongServer(srv, &PingPongServer{})
lis, err := net.Listen("tcp", ":1234")
if err != nil {
log.Fatal(err)
}
go func() {
if err := srv.Serve(lis); err != nil && err != io.EOF {
log.Fatal(err)
}
}()
}
// 关闭server的函数
func stopServer() {
srv.GracefulStop()
}
// 定义一个结构体来保存测试相关的上下文信息,比如WaitGroup
type TestContext struct {
wg sync.WaitGroup
}
// 初始化测试上下文
func NewTestContext() *TestContext {
return &TestContext{
wg: sync.WaitGroup{},
}
}
// 测试结束时的清理函数,用于关闭服务端
func (tc *TestContext) Cleanup() {
stopServer()
tc.wg.Wait()
}
func TestPingV1(t *testing.T) {
ctx := NewTestContext()
defer ctx.Cleanup()
// 在测试的时候启动服务端,虽然省事,不是不建议
startServer()
tests := []struct {
name string
}{
{name: "ping"},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ctx.wg.Add(1)
log.Println("ping")
Ping()
ctx.wg.Done()
})
}
}
ping\clinet_v2_test.go
package main
import (
"fmt"
"log"
"os"
"testing"
)
// 产生测试文件
func TestPingV2(t *testing.T) {
// 创建一个日志文件
logFile, err := os.OpenFile("test.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
if err != nil {
log.Fatalf("无法创建日志文件: %v", err)
}
defer logFile.Close()
// 设置日志输出到文件
log.SetOutput(logFile)
tests := []struct {
name string
}{
{name: "ping"},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
log.Println("ping")
fmt.Println("Ping result:") // 打印Ping函数的返回结果
Ping()
})
}
}
ping\server.go
package main
import (
"context"
"fmt"
"io"
"log"
"net"
"google.golang.org/grpc"
pb "github.com/jergoo/go-grpc-tutorial/protos/ping" // 引入编译生成的包
)
// PingPongServer 实现 pb.PingPongServer 接口
type PingPongServer struct {
pb.UnimplementedPingPongServer // 兼容性需要,避免未实现server接口全部方法
}
// Ping 单次请求-响应模式
func (s *PingPongServer) Ping(ctx context.Context, req *pb.PingRequest) (*pb.PongResponse, error) {
println("server: receive ping")
return &pb.PongResponse{Value: "pong"}, nil
}
// MultiPong 服务端流模式
func (s *PingPongServer) MultiPong(req *pb.PingRequest, stream pb.PingPong_MultiPongServer) error {
for i := 0; i < 10; i++ {
data := &pb.PongResponse{Value: "pong"}
// 发送消息
err := stream.Send(data)
if err != nil {
return err
}
}
return nil
}
// MultiPing 客户端流模式
func (s *PingPongServer) MultiPing(stream pb.PingPong_MultiPingServer) error {
msgs := []string{}
for {
// 提前结束接收消息
if len(msgs) > 5 {
return stream.SendAndClose(&pb.PongResponse{Value: "ping enough, max 5"})
}
msg, err := stream.Recv()
if err != nil {
// 客户端消息结束,返回响应信息
if err == io.EOF {
return stream.SendAndClose(&pb.PongResponse{Value: fmt.Sprintf("got %d ping", len(msgs))})
}
return err
}
msgs = append(msgs, msg.Value)
}
}
// MultiPingPong 双向流模式
func (s *PingPongServer) MultiPingPong(stream pb.PingPong_MultiPingPongServer) error {
msgs := []string{}
for {
// 接收消息
msg, err := stream.Recv()
if err != nil {
if err == io.EOF {
break
}
return err
}
msgs = append(msgs, msg.Value)
// 每收到两个消息响应一次
if len(msgs)%2 == 0 {
err = stream.Send(&pb.PongResponse{Value: "pong"})
if err != nil {
return err
}
}
}
return nil
}
// 启动server
func main() {
srv := grpc.NewServer()
// 注册 PingPongServer
pb.RegisterPingPongServer(srv, &PingPongServer{})
lis, err := net.Listen("tcp", ":1234")
if err != nil {
log.Fatal(err)
}
log.Println("listen on 1234")
srv.Serve(lis)
}
标签:pb,log,err,打印,单元测试,golang,func,go
From: https://www.cnblogs.com/zhuoss/p/18666278