怪奇物语

怪奇物语

首页 新随笔 联系 管理

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)
}

posted on 2025-01-11 22:11  超级无敌美少男战士  阅读(177)  评论(0)    收藏  举报