Skip to content

Migrating from the legacy SDK API v3.16.0 ​

Removed in v3.20.0

The legacy API (sdk.Run, mock.Stub, mock.Verify, …) no longer exists. This page is the map from each old construct to its replacement.

Why the change? ​

The original API grew organically — one generic mock.Stub() for every gRPC pattern, .Commit() to finish every chain, sdk.By() to reference methods, and verification buried under mock.Verify().Method(...).

It worked, but it had friction:

  • mock.Stub() + .When() + .Reply() treated unary, streaming, and bidi the same — the reader couldn't tell which pattern was being tested without scanning the whole chain
  • Commit() was easy to forget, silently breaking expectations
  • .When(sdk.Equals("k", "v")) was verbose — Equals is the default, so Match("k", "v") says the same thing in half the characters
  • .Reply(sdk.Data("k", "v")) → .Return("k", "v") — less nesting, less noise
  • sdk.By() was an unnecessary wrapper; full method strings are unambiguous
  • Verification required a detour through mock.Verify(), adding ceremony to a simple assertion
  • sdk.Run() returned an error that was always checked with require.NoError — noise, never a real error in practice

v3.16.0 ditches the generic approach. Every gRPC interaction pattern gets its own expectation type, verification moves to direct server methods, and Commit() disappears entirely.

Quick Reference ​

Server creation ​

go
// old
mock, err := sdk.Run(t, sdk.WithFileDescriptor(proto))
require.NoError(t, err)
defer mock.Close()

// new
srv := sdk.NewServer(t, sdk.WithFileDescriptor(proto))
defer srv.Close()

Unary ​

go
// old
mock.Stub(sdk.By(helloworld.Greeter_SayHello_FullMethodName)).
    When(sdk.Equals("name", "Alex")).
    Reply(sdk.Data("message", "Hi Alex")).
    Commit()

// new
srv.ExpectUnary(helloworld.Greeter_SayHello_FullMethodName).
    Match("name", "Alex").
    Return("message", "Hi Alex")

Server-stream ​

go
// old
mock.Stub(sdk.By(FullMethod)).
    When(sdk.Equals("query", "test")).
    ReplyStream(
        sdk.Data("id", "1"),
        sdk.Data("id", "2"),
    ).
    Commit()

// new
srv.ExpectServerStream(FullMethod).
    Match("query", "test").
    SendStream(
        map[string]any{"id": "1"},
        map[string]any{"id": "2"},
    )

Client-stream ​

go
// old
mock.Stub(sdk.By(FullMethod)).
    WhenStream("value", "\\d+").
    Reply(sdk.Data("result", 42.0)).
    Commit()

// new
srv.ExpectClientStream(FullMethod).
    Match(sdk.Matches("value", "\\d+")).
    Return("result", 42.0)

Bidirectional ​

go
// old
mock.Stub(sdk.By(FullMethod)).ReplyStream().Commit()

// new
srv.ExpectBidirectionalStream(FullMethod).
    Run(func(ctx context.Context, stream any) error {
        return nil
    })

Mapping Table ​

OldNew
sdk.Run(t, opts...)sdk.NewServer(t, opts...)
mock.Stub(svc, method)srv.ExpectUnary("/svc/Method")
mock.Stub(sdk.By(fullMethod))srv.ExpectUnary(fullMethod)
.When(sdk.Equals("k", "v"))Match("k", "v")
.When(sdk.Contains("k", "v"))Match(sdk.Contains("k", "v"))
.When(sdk.Matches("k", "re"))Match(sdk.Matches("k", "re"))
.WhenHeaders(sdk.Equals("k", "v"))WithHeader(sdk.Equals("k", "v"))
.Reply(sdk.Data("k", "v"))Return("k", "v")
.ReplyError(code, msg)ReturnError(code, msg)
.ReplyStream(msg...)SendStream(map...) (on ExpectServerStream)
.Delay(d).Commit()Return(sdk.Delay(d, kv...))
.Times(n).Commit()Times(n).Return(...)
.Priority(n).Commit()Priority(n).Return(...)
.Commit()removed — auto-registered
mock.Verify().Method(fm).Called(t, n)require.Equal(t, n, srv.Called(fm))
mock.Verify().Total(t, n)require.Equal(t, n, srv.TotalCalls())
mock.Verify().VerifyStubTimes(t)srv.ExpectationsWereMet()
mock.History().FilterByMethod(...)srv.History()
mock.Conn()srv.Conn()
mock.Close()srv.Close()
sdk.By(fullMethod)use full method string directly
sdk.StubBatch()NextWillReturn(kv...)

By Example ​

Matching ​

go
// old — verbose, even for default Equals
.When(sdk.Equals("name", "Alex"))
.When(sdk.Contains("name", "partial"))
.WhenHeaders(sdk.Equals("authorization", "Bearer token"))

// new — Equals is the default for payload, WithHeader for headers
.Match("name", "Alex")
.Match(sdk.Contains("name", "partial"))
.WithHeader(sdk.Equals("authorization", "Bearer token"))

Delays ​

go
// old — delay was a separate modifier before Commit
mock.Stub(sdk.By(FullMethod)).
    When(sdk.Equals("id", "slow")).
    Reply(sdk.Data("result", "ok")).
    Delay(100 * time.Millisecond).
    Commit()

// new — delay is part of the return value
srv.ExpectUnary(FullMethod).
    Match("id", "slow").
    Return(sdk.Delay(100*time.Millisecond, "result", "ok"))

Sequential responses ​

go
// old — no clean way, needed StubBatch or multiple stubs

// new — NextWillReturn chains additional responses
srv.ExpectUnary(FullMethod).
    Match("id", "seq").
    Return("step", "first").
    NextWillReturn("step", "second")

// NextWillReturnError for error sequences
srv.ExpectUnary(FullMethod).
    Match("id", "retry").
    ReturnError(codes.Unavailable, "try again").
    NextWillReturnError(codes.Unavailable, "one more").
    NextWillReturn("result", "success")

Effects ​

go
// new — side-effect stubs, no equivalent in old API
effect := sdk.Upsert("svc", "NextMethod").
    Match("step", "complete").
    Return("status", "done").
    Build()

srv.ExpectUnary("/svc/Method").
    Match("step", "begin").
    Effect(effect).
    Return("status", "started")

Verification ​

go
// old — detour through mock.Verify()
mock.Verify().Method(sdk.By(FullMethod)).Called(t, 2)
mock.Verify().Total(t, 5)
mock.Verify().Method(sdk.By(FullMethod)).Never(t)
mock.History().FilterByMethod("svc", "method")

// new — direct server methods
require.Equal(t, 2, srv.Called(FullMethod))
require.Equal(t, 5, srv.TotalCalls())
require.Equal(t, 0, srv.Called(FullMethod))
calls := srv.History()

What Was Removed ​

  • sdk.By() — not needed, full method strings work directly
  • Commit() — auto-registered on terminal call
  • sdk.StubBatch() — replaced by NextWillReturn
  • sdk.MergeHeaders() / sdk.MergeOutput() — replaced by unified Match() API
  • WithPayloadFunc() — was a no-op placeholder
  • mock.Verify() — replaced by srv.Called(), srv.TotalCalls(), srv.History()

What's New ​

  • ExpectUnary(fullMethod) — dedicated expectations for each gRPC pattern
  • ExpectServerStream(fullMethod) + SendStream()
  • ExpectClientStream(fullMethod) + Match()
  • ExpectBidirectionalStream(fullMethod) + Run(fn)
  • NextWillReturn(kv...) — sequential responses on any expectation
  • Return(sdk.Delay(d, kv...)) — composable delay inside the return value
  • Effect(Upsert(...).Build()) — side-effect stubs
  • Server.Reset() / Server.Flush() / Server.Address() — more lifecycle methods
  • WithBatch() — batch mode for remote
  • Unified Matcher types: Equals, Contains, Matches, Glob, AnyOf, And, IgnoreArrayOrder

Removed in v3.20.0 ​

RemovedReplacement
sdk.Run(t, opts...)sdk.NewServer(t, opts...)
sdk.Mock*sdk.Server
sdk.StubBuilderExpectUnary / ExpectServerStream / ExpectClientStream / ExpectBidirectionalStream
sdk.Verifier, sdk.MethodVerifierServer.Called, Server.TotalCalls, Server.ExpectationsWereMet
sdk.HistoryReaderServer.History()
sdk.Merge(...)sdk.And(...)
Server.Stub, Server.Verifythe four Expect* methods and the verification methods above
Server.Addr()Server.Address()