Documentation ¶
Overview ¶
Package Gopactor provides a set of tools to simplify testing of actors created with Protoactor (https://github.com/AsynkronIT/protoactor-go).
Main features:
Intercept messages ¶
For any actor you want to test, Gopactor can intercept all it's inbound and outbound messages. It is probably exactly what you want to do when you test the actor's behavior. Moreover, interception forces a naturally asynchronous actor to act in a more synchronous way. When messages are sent and received under the control of Gopactor, it is much easier to reason about the actor's logic and examine its communication with the outside world step by step.
Intercept system messages ¶
Protoactor uses special system messages to control the lifecycle of an actor. Gopactor can intercept some of such messages to help you ensure that your actor stops or restarts when expected.
Intercept spawning of children ¶
It is a common pattern to let actors spawn child actors and communicate with them. Good as it is, this pattern often stays in the way of writing deterministic tests. Given that child-spawning and communication happen in the background asynchronously, it can be seen more like a side-effect that can interfere with your tests in many unpredictable ways.
By default, Gopactor intercepts all spawn invocations and instead of spawning what is requested, it spawns no-op null-actors. These actors are guaranteed to not communicate with their parents in any way. If you do no want Gopactor to substitute spawned actors, you can easily disable this behavior via configuration options.
Goconvey-style assertions ¶
Gopactor provides a bunch of assertion functions to be used with the popular testing framework Goconvey (http://goconvey.co/). For instance,
So(worker, ShouldReceive, "ping") So(worker, ShouldSendTo, requestor, "pong")
Configurable ¶
For every tested actor, you can define what you want to intercept: inbound, outbound or system messages. Or spawning of children. Or everything. Or nothing at all. You can also set a custom timeout:
options := OptNoInterception. WithOutboundInterception(). WithPrefix("my-actor"). WithTimeout(10 * time.Millisecond)
Example of usage ¶
Here is a short example. We'll define and test a simple worker actor that can do only one thing: respond "pong" when it receives "ping".
package worker_test import ( "testing" "github.com/AsynkronIT/protoactor-go/actor" . "github.com/meAmidos/gopactor" . "github.com/smartystreets/goconvey/convey" ) // Actor to test type Worker struct{} // This actor is very simple. It can do only one thing, but it does this thing well. func (w *Worker) Receive(ctx actor.Context) { switch m := ctx.Message().(type) { case string: if m == "ping" { ctx.Respond("pong") } } } func TestWorker(t *testing.T) { Convey("Test the worker actor", t, func() { // It is essential to spawn the tested actor using Gopactor. This way, Gopactor // will be able to intercept all inbound/outbound messages of the actor. worker, err := SpawnFromInstance(&Worker{}, OptDefault.WithPrefix("worker")) So(err, ShouldBeNil) // Spawn an additional actor that will communicate with our worker. // The only purpose of this actor is to be a sparring partner, // so we don't care about its functionality. // Conveniently, Gopactor provides an easy way to create it. requestor, err := SpawnNullActor() So(err, ShouldBeNil) // Let the requestor ping the worker worker.Request("ping", requestor) // Assert that the worker receives the ping message So(worker, ShouldReceive, "ping") // Assert that the worker sends back the correct response So(worker, ShouldSendTo, requestor, "pong") // Finally, assert that the requestor gets the response So(requestor, ShouldReceive, "pong") }) }
Index ¶
- Variables
- func PactReset()
- func SpawnFromFunc(f actor.ActorFunc, opts ...options.Options) (*actor.PID, error)
- func SpawnFromInstance(obj actor.Actor, opts ...options.Options) (*actor.PID, error)
- func SpawnFromProducer(producer actor.Producer, opts ...options.Options) (*actor.PID, error)
- func SpawnNullActor(opts ...options.Options) (*actor.PID, error)
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ( OptNoInterception = options.OptNoInterception OptDefault = options.OptDefault OptOutboundInterceptionOnly = options.OptOutboundInterceptionOnly OptInboundInterceptionOnly = options.OptInboundInterceptionOnly )
Configuration options are explained in detail in the documentation for the options package: https://godoc.org/github.com/meAmidos/gopactor/options
var ( ShouldReceive = assertions.ShouldReceive ShouldReceiveFrom = assertions.ShouldReceiveFrom ShouldReceiveSomething = assertions.ShouldReceiveSomething ShouldReceiveN = assertions.ShouldReceiveN ShouldSend = assertions.ShouldSend ShouldSendTo = assertions.ShouldSendTo ShouldSendSomething = assertions.ShouldSendSomething ShouldSendN = assertions.ShouldSendN ShouldNotSendOrReceive = assertions.ShouldNotSendOrReceive ShouldStart = assertions.ShouldStart ShouldStop = assertions.ShouldStop ShouldBeRestarting = assertions.ShouldBeRestarting ShouldObserveTermination = assertions.ShouldObserveTermination ShouldSpawn = assertions.ShouldSpawn )
These assertions are mostly self-explanatory, but it may be helpful to go through some examples which can be found in the documentation for the assertions package: https://godoc.org/github.com/meAmidos/gopactor/assertions
Functions ¶
func PactReset ¶
func PactReset()
PactReset cleans up internal data structures used by Gopactor. Normally, you do not have to use it. If you just test a dozen of actors in a short-living test, there is no need to care about cleaning up. However, if for some reason, you are spawning thousands of actors in a long-running test, you might want to call this function from time to time.
func SpawnFromFunc ¶
Analog of Protoactor's actor.SpawnPrefix(actor.FromFunc(...))
Example ¶
f := func(ctx actor.Context) { if msg, ok := ctx.Message().(string); ok { fmt.Printf("Got a message: %s\n", msg) } } worker, _ := SpawnFromFunc(f) worker.Tell("Hello, world!") ShouldReceiveSomething(worker)
Output: Got a message: Hello, world!
func SpawnFromInstance ¶
Analog of Protoactor's actor.SpawnPrefix(actor.FromInstance(...)) The main difference is that after spawning with Gopactor you can write assertions for the spawned actor.
Example ¶
// Given that the Worker actor is defined elsewhere worker, err := SpawnFromInstance(&Worker{}) if err != nil { log.Print("Failed to spawn a worker") return } worker.Tell("Hello, world!")
Output:
func SpawnFromProducer ¶
Analog of Protoactor's actor.SpawnPrefix(actor.FromProducer(...))
Example ¶
producer := func() actor.Actor { return &Worker{} } worker, _ := SpawnFromProducer(producer) worker.Tell("Hello, world!")
Output:
func SpawnNullActor ¶
Spawn an actor that does nothing. It can be very useful in tests when all you need is an actor that can play a role of a message sender and a black-hole receiver.
Example ¶
worker, _ := SpawnFromInstance(&Worker{}) requestor, _ := SpawnNullActor() worker.Request("ping", requestor)
Output:
Types ¶
This section is empty.
Directories ¶
Path | Synopsis |
---|---|
Goconvey-style assertions to be used with actors spawned using Gopactor.
|
Goconvey-style assertions to be used with actors spawned using Gopactor. |
Package options defines configuration options that are used by Gopactor when spawning actors.
|
Package options defines configuration options that are used by Gopactor when spawning actors. |