You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

154 lines
5.4 KiB

  1. // Copyright 2017 Google LLC
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. /*
  15. Package rpcreplay supports the capture and replay of gRPC calls. Its main goal is
  16. to improve testing. Once you capture the calls of a test that runs against a real
  17. service, you have an "automatic mock" that can be replayed against the same test,
  18. yielding a unit test that is fast and flake-free.
  19. This package is EXPERIMENTAL and subject to change without notice.
  20. Recording
  21. To record a sequence of gRPC calls to a file, create a Recorder and pass its
  22. DialOptions to grpc.Dial:
  23. rec, err := rpcreplay.NewRecorder("service.replay", nil)
  24. if err != nil { ... }
  25. defer func() {
  26. if err := rec.Close(); err != nil { ... }
  27. }()
  28. conn, err := grpc.Dial(serverAddress, rec.DialOptions()...)
  29. It is essential to close the Recorder when the interaction is finished.
  30. There is also a NewRecorderWriter function for capturing to an arbitrary
  31. io.Writer.
  32. Replaying
  33. Replaying a captured file looks almost identical: create a Replayer and use
  34. its DialOptions. (Since we're reading the file and not writing it, we don't
  35. have to be as careful about the error returned from Close).
  36. rep, err := rpcreplay.NewReplayer("service.replay")
  37. if err != nil { ... }
  38. defer rep.Close()
  39. conn, err := grpc.Dial(serverAddress, rep.DialOptions()...)
  40. Since a real connection isn't necessary for replay, you can get a fake
  41. one from the replayer instead of calling grpc.Dial:
  42. rep, err := rpcreplay.NewReplayer("service.replay")
  43. if err != nil { ... }
  44. defer rep.Close()
  45. conn, err := rep.Connection()
  46. Initial State
  47. A test might use random or time-sensitive values, for instance to create unique
  48. resources for isolation from other tests. The test therefore has initial values, such
  49. as the current time, or a random seed, that differ from run to run. You must record
  50. this initial state and re-establish it on replay.
  51. To record the initial state, serialize it into a []byte and pass it as the second
  52. argument to NewRecorder:
  53. timeNow := time.Now()
  54. b, err := timeNow.MarshalBinary()
  55. if err != nil { ... }
  56. rec, err := rpcreplay.NewRecorder("service.replay", b)
  57. On replay, get the bytes from Replayer.Initial:
  58. rep, err := rpcreplay.NewReplayer("service.replay")
  59. if err != nil { ... }
  60. defer rep.Close()
  61. err = timeNow.UnmarshalBinary(rep.Initial())
  62. if err != nil { ... }
  63. Callbacks
  64. Recorders and replayers have support for running callbacks before messages are
  65. written to or read from the replay file. A Recorder has a BeforeFunc that can modify
  66. a request or response before it is written to the replay file. The actual RPCs sent
  67. to the service during recording remain unaltered; only what is saved in the replay
  68. file can be changed. A Replayer has a BeforeFunc that can modify a request before it
  69. is sent for matching.
  70. Example uses for these callbacks include customized logging, or scrubbing data before
  71. RPCs are written to the replay file. If requests are modified by the callbacks during
  72. recording, it is important to perform the same modifications to the requests when
  73. replaying, or RPC matching on replay will fail.
  74. A common way to analyze and modify the various messages is to use a type switch.
  75. // Assume these types implement proto.Message.
  76. type Greeting struct {
  77. line string
  78. }
  79. type Farewell struct {
  80. line string
  81. }
  82. func sayings(method string, msg proto.Message) error {
  83. switch m := msg.(type) {
  84. case Greeting:
  85. msg.line = "Hi!"
  86. return nil
  87. case Farewell:
  88. msg.line = "Bye bye!"
  89. return nil
  90. default:
  91. return fmt.Errorf("unknown message type")
  92. }
  93. }
  94. Nondeterminism
  95. A nondeterministic program may invoke RPCs in a different order each time
  96. it is run. The order in which RPCs are called during recording may differ
  97. from the order during replay.
  98. The replayer matches incoming to recorded requests by method name and request
  99. contents, so nondeterminism is only a concern for identical requests that result
  100. in different responses. A nondeterministic program whose behavior differs
  101. depending on the order of such RPCs probably has a race condition: since both the
  102. recorded sequence of RPCs and the sequence during replay are valid orderings, the
  103. program should behave the same under both.
  104. Other Replayer Differences
  105. Besides the differences in replay mentioned above, other differences may cause issues
  106. for some programs. We list them here.
  107. The Replayer delivers a response to an RPC immediately, without waiting for other
  108. incoming RPCs. This can violate causality. For example, in a Pub/Sub program where
  109. one goroutine publishes and another subscribes, during replay the Subscribe call may
  110. finish before the Publish call begins.
  111. For streaming RPCs, the Replayer delivers the result of Send and Recv calls in
  112. the order they were recorded. No attempt is made to match message contents.
  113. At present, this package does not record or replay stream headers and trailers, or
  114. the result of the CloseSend method.
  115. */
  116. package rpcreplay // import "cloud.google.com/go/rpcreplay"