用 net/rpc 搭建本地 RPC 服务做单元测试的最轻量方式是使用 bytes.Buffer 模拟传输层,配合 gob 编解码,无需网络端口;需确保结构体字段导出、使用独立读写 buffer、正确启停协程,并注意 codec 一致性与 goroutine 生命周期管理。
net/rpc 搭建本地 RPC 服务做单元测试Go 标准库的 net/rpc 支持内存通道(rpc.NewServer() + server.ServeCodec())直接对接 gob.NewEncoder/Decoder,无需启动 TCP 端口就能完成完整 RPC 流程。这是本地测试最轻量、最可控的方式。
gob 编码失败bytes.Buffer 替代网络连接实现零开销编解码RPC 的核心是请求序列化 → 传输 → 反序列化 → 执行 → 返回序列化 → 传输 → 反序列化。用 bytes.Buffer 模拟“传输层”,能跳过 socket 创建、超时、重连等无关逻辑,只验证业务协议是否正确。
buf := new(bytes.Buffer)
server := rpc.NewServer()
server.RegisterName("Arith", &Arith{})
codec := gob.NewGobServerCodec(buf, buf) // 注意:两个 buf 分别用于读/写
go server.ServeCodec(codec)
// 客户端复用同一个 buf 做通信
client := rpc.NewClientWithCodec(gob.NewGobClientCodec(buf, buf))
defer client.Close()
args := &ArithArgs{A: 10, B: 3}
var reply ArithReply
err := client.Call("Arith.Multiply", args, &reply)
bytes.Buffer 实例(或一个但显式 Reset()),否则读写位置冲突导致 panicgob 是 Go 默认 codec,若服务端用 jsonrpc,客户端也得用 jsonrpc.NewClientCodec
server.ServeCodec() 启动协程,会导致 Call 阻塞或立即返回 io.EOF
标准 net/rpc 不支持服务端主动推消息。若业务中有类似 Subscribe(req *Req, stream Stream) 这种伪流式接口,本地测试时需把 stream 替换为自定义结构体,内部用 chan 模拟推送行为。
type MockStream struct {
ch chan interface{}
}
func (m *MockStream) Send(v interface{}) error {
m.ch <- v
return nil
}
// 在测试中:
stream := &MockStream{ch: make(chan interface{}, 10)}
go func() {
// 模拟服务端往 stream 写数据
stream.Send(&Event{ID: "e1"})
stream.Send(&Event{ID: "e2"})
}()
// 客户端从 stream 接收
for i := 0; i < 2; i++ {
select {
case ev := <-stream.ch:
// 处理事件
}
}
Send 会阻塞,测试卡死Send,否则同步调用会等客户端接收,形成死锁如果只是想绕过网络调用验证上层逻辑(比如某个 handler 里调用了 userSvc.GetUser()),直接定义一个符合接口的 mock 结构体比启动本地 RPC 服务更轻量。
type MockUserSvc struct {
GetUserFunc func(ctx context.Context, id int64) (*User, error)
}
func (m MockUserSvc) GetUser(ctx context.Context, id int64) (User, error) {
return m.GetUserFunc(ctx, id)
}
// 测试中:
svc := &MockUserSvc{
GetUserFunc: func(ctx context.Context, id int64) (*User, error) {
return &User{Name: "test"}, nil
},
}
handler := NewHandler(svc)
// 调用 handler 方法,内部会走 mock 的 GetUser

本地测试 RPC 最容易忽略的是 codec 一致性与 goroutine 生命周期管理——服务端没启协程、客户端没关连接、buffer 读写错位,都会让测试看似“跑通”实则没触发任何业务逻辑。