I have this unit test:
func TestServer(t *testing.T) {
db := prepareDBConn(t)
defer db.Close()
lis := bufconn.Listen(1024 * 1024)
t.Logf("Opened listener: %v", lis)
grpcServer := grpc.NewServer(
withUnaryInterceptor(),
)
t.Logf("Opened grpc server: %v", grpcServer)
signKey := getSignKey()
if signKey == nil {
t.Fatal("Failed to find or parse RSA private key")
}
verifyKeyErr := setVerifyKey()
if verifyKeyErr != nil {
t.Fatal("Failed to find or parse RSA public key")
}
pb.RegisterAuthServiceServer(grpcServer, newAuthServiceServer(db, signKey))
err := grpcServer.Serve(lis)
if err != nil {
t.Fatalf("Failed to listen: %+v", err)
}
}
If I comment out the last 4 lines:
// err := grpcServer.Serve(lis)
// if err != nil {
// t.Fatalf("Failed to listen: %+v", err)
// }
it will pass every single time, but for whatever reason it gets suck at the line err := grpcServer.Server(lis)
. I've tried using a real TCP listener (swapping lis := bufconn.Listen
with lis := net.Listen("tcp")
. The output of the test (when it fails) is long and ambiguous:
panic: test timed out after 2m0s
goroutine 34 [running]:
testing.(*M).startAlarm.func1()
/usr/local/Cellar/go/1.12.7/libexec/src/testing/testing.go:1334 +0xdf
created by time.goFunc
/usr/local/Cellar/go/1.12.7/libexec/src/time/sleep.go:169 +0x44
goroutine 1 [chan receive]:
testing.(*T).Run(0xc000156100, 0x15cb741, 0xa, 0x15ed1a8, 0x10b38a6)
/usr/local/Cellar/go/1.12.7/libexec/src/testing/testing.go:917 +0x381
testing.runTests.func1(0xc000156000)
/usr/local/Cellar/go/1.12.7/libexec/src/testing/testing.go:1157 +0x78
testing.tRunner(0xc000156000, 0xc0000c1e10)
/usr/local/Cellar/go/1.12.7/libexec/src/testing/testing.go:865 +0xc0
testing.runTests(0xc00000e0c0, 0x1a53d60, 0x3, 0x3, 0x0)
/usr/local/Cellar/go/1.12.7/libexec/src/testing/testing.go:1155 +0x2a9
testing.(*M).Run(0xc00013e000, 0x0)
/usr/local/Cellar/go/1.12.7/libexec/src/testing/testing.go:1072 +0x162
_/path/to/my/server.TestMain(0xc00013e000)
/path/to/my/server/main_test.go:102 +0x2b
main.main()
_testmain.go:44 +0x13e
goroutine 19 [chan receive]:
github.com/golang/glog.(*loggingT).flushDaemon(0x1a5f540)
/my/gopath/src/github.com/golang/glog/glog.go:882 +0x8b
created by github.com/golang/glog.init.0
/my/gopath/src/github.com/golang/glog/glog.go:410 +0x272
goroutine 5 [select]:
google.golang.org/grpc/test/bufconn.(*Listener).Accept(0xc00000e0e0, 0x15edf50, 0xc000176000, 0xc00000e420, 0x0)
/my/gopath/src/google.golang.org/grpc/test/bufconn/bufconn.go:51 +0xaf
google.golang.org/grpc.(*Server).Serve(0xc000176000, 0x1684880, 0xc00000e0e0, 0x0, 0x0)
/my/gopath/src/google.golang.org/grpc/server.go:586 +0x1f2
_/path/to/my/server.TestServer(0xc000156100)
/path/to/my/server/main_test.go:67 +0x309
testing.tRunner(0xc000156100, 0x15ed1a8)
/usr/local/Cellar/go/1.12.7/libexec/src/testing/testing.go:865 +0xc0
created by testing.(*T).Run
/usr/local/Cellar/go/1.12.7/libexec/src/testing/testing.go:916 +0x35a
goroutine 6 [select]:
database/sql.(*DB).connectionOpener(0xc00013a0c0, 0x1685780, 0xc00006a300)
/usr/local/Cellar/go/1.12.7/libexec/src/database/sql/sql.go:1000 +0xe8
created by database/sql.OpenDB
/usr/local/Cellar/go/1.12.7/libexec/src/database/sql/sql.go:670 +0x15e
goroutine 7 [select]:
database/sql.(*DB).connectionResetter(0xc00013a0c0, 0x1685780, 0xc00006a300)
/usr/local/Cellar/go/1.12.7/libexec/src/database/sql/sql.go:1013 +0xfb
created by database/sql.OpenDB
/usr/local/Cellar/go/1.12.7/libexec/src/database/sql/sql.go:671 +0x194
exit status 2
It doesn't tell me what went wrong (except for that it timed out) and seems to reference tests for other libraries as well? I'm invoking the test via vs-code
, it uses this command:
/usr/local/bin/go test -timeout 30s -run ^(TestServer)$
I can build and run the package, it works exactly as I expect and does not fail at that line, so I think I am doing something incorrectly in regards to unit tests but I don't have much to go on this point and I don't know much about go testing.
Serve is a blocking call. It will sit and wait for new connections and handle them as they come in, indefinitely. That's its whole purpose - it doesn't return until the server shuts down or crashes. Per the documentation for Serve
:
Serve accepts incoming connections on the listener lis, creating a new ServerTransport and service goroutine for each. The service goroutines read gRPC requests and then call the registered handlers to reply to them. Serve returns when lis.Accept fails with fatal errors.
To your other questions:
It doesn't tell me what went wrong (except for that it timed out)
That's what went wrong. go test
has a time limit, and the test exceeded it. There's not really anything more for it to tell. It provided stack traces, which can be helpful in determining why it timed out, because they tell you where each goroutine was when it timed out (e.g. goroutine 5 was sitting waiting for new connections in Listener.Accept
).
seems to reference tests for other libraries as well
Not tests (those would be in _test.go
files), just code from libraries your code references. They're stack traces, which typically do include code from the standard library and/or 3rd-party libraries, because it's part of the call stack for that goroutine at the time of the trace.
As I can see, you're trying to unit test a gRPC server. The strategy you're trying to use is rather overkill for unit testing.
For simplicity, you should have a Server struct that holds the inter-connected pieces, for this example: the db connection.
So you'll just initialize the db connection and pass it to the Server struct. This is the right place to mock the db. After that, you'll have the spin up gRPC server instance, since you don't need to dial gRPC for unit testing purpose.
Then you can just call the intended rpc functions on the initialized Server instance. It looks way more cleaner and maintainable.