Pub / Sub black box testing in Go (golang)

Actually I have an ecosystem of around 30 micro services communicating through a messaging system (NATS).

Basically what a micro service does on our architecture is:

  • Receives a specific subject message.
  • Run some algorithms with the message
  • Publishes a new message with a different body and subject.

Isolate and unit test

A simple micro service with this approach may look like this:

nc, _ := nats.Connect(nats.DefaultURL)

nc.Subscribe("testme", func(m *Msg) {
if m.Data == "foo" {
nc.Publish("foo", []byte("bar"))
} else {
nc.Publish("foo", []byte("foobar"))
}
})

This is a really simple example, in this case the recommended approach could be to isolate the code on an external function and unit test this function. So, after the refactor the code could look like this

nc, _ := nats.Connect(nats.DefaultURL)

nc.Subscribe("testme", func(m *Msg) {
s, b := process(m.Data)
nc.Publish(s, b)
})

func process(m []byte) (s string, b []byte) {
if m == "foo" {
return "foo", []byte("bar")
}
return "foo", []byte("foobar")
}

Black box test

Depending on the logic inside your code, the previous testing approach may be difficult to apply.

For example you have an input message but the output can be, one or multiple messages, this is not easy to isolate as the previous exercise.

func foobar(nc *nats.Conn) {
nc.Subscribe("testme", func(m *Msg) {
if m.Data == "foo" {
nc.Publish("foo", []byte("bar"))
nc.Publish("logs", []byte("some logs here"))
} else {
nc.Publish("foo", []byte("foobar"))
}
})
}

Regarding to the previous example we can write some black box tests. On these tests you basically publish a message and wait for an expected message to be received.

package main

import (
"testing"
"time"

"github.com/nats-io/nats"
)

func wait(ch chan bool) error {
return waitTime(ch, 500*time.Millisecond)
}

func waitTime(ch chan bool, timeout time.Duration) error {
select {
case <-ch:
return nil
case <-time.After(timeout):
}
return errors.New("timeout")
}


func TestMe(t *testing.T) {
nc, _ := nats.Connect(nats.DefaultURL)
foobar(nc)

ch := make(chan bool)
n.Subscribe("logs", func(msg *nats.Msg) {
ch <- true
})

n.Publish("testme", []byte(`foo`))
if e := wait(ch); e != nil {
t.Fatal("Message not received from nats for subscription")
}
}

You can find a working example on the tests of this library ;)

One clap, two clap, three clap, forty?

By clapping more or less, you can signal to us which stories really stand out.