golang 单元测试 命令行 日志打印 测试结果打印控制台
test.bat
| |
| |
| @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" |
| ) |
| |
| |
| 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) |
| } |
| |
| |
| func MultiPong() { |
| conn, err := grpc.Dial("localhost:1234", grpc.WithInsecure()) |
| if err != nil { |
| log.Fatal(err) |
| } |
| defer conn.Close() |
| |
| |
| client := pb.NewPingPongClient(conn) |
| |
| 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) |
| } |
| } |
| |
| |
| 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) |
| } |
| |
| |
| 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) |
| } |
| |
| |
| 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 |
| |
| |
| 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) |
| } |
| }() |
| } |
| |
| |
| func stopServer() { |
| srv.GracefulStop() |
| } |
| |
| |
| 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\server.go
| |
| package main |
| |
| import ( |
| "context" |
| "fmt" |
| "io" |
| "log" |
| "net" |
| |
| "google.golang.org/grpc" |
| |
| pb "github.com/jergoo/go-grpc-tutorial/protos/ping" |
| ) |
| |
| |
| type PingPongServer struct { |
| pb.UnimplementedPingPongServer |
| } |
| |
| |
| func (s *PingPongServer) Ping(ctx context.Context, req *pb.PingRequest) (*pb.PongResponse, error) { |
| println("server: receive ping") |
| return &pb.PongResponse{Value: "pong"}, nil |
| } |
| |
| |
| 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 |
| } |
| |
| |
| 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) |
| } |
| } |
| |
| |
| 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 |
| } |
| |
| |
| func main() { |
| srv := grpc.NewServer() |
| |
| pb.RegisterPingPongServer(srv, &PingPongServer{}) |
| lis, err := net.Listen("tcp", ":1234") |
| if err != nil { |
| log.Fatal(err) |
| } |
| log.Println("listen on 1234") |
| srv.Serve(lis) |
| } |
| |
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 清华大学推出第四讲使用 DeepSeek + DeepResearch 让科研像聊天一样简单!
· 推荐几款开源且免费的 .NET MAUI 组件库
· 实操Deepseek接入个人知识库
· 易语言 —— 开山篇
· Trae初体验