I have a function that, when run as a goroutine, receives messages,
process them, and possibly sends messages back.
I want to unit-test this function. I want to test that, given certain
inputs, I receive a correct message back. I also want to test that,
given different inputs, I don't receive any messages.
Here's a bare-bones example showing what I'm trying to do:
http://play.golang.org/p/KYBNXtlDrM
So, as you can see, that code won't work because it can't catch the
error case of the function not sending a message when it should. So
how do I test that function?
I can think of some options.
1: Use a timeout. If it times out before we received a message, then
we assume it never sent a message. This seems problematic to me,
because how do we know the goroutine wasn't just taking its time? How
do I know how long to make the timeout for? Too long and my unit
tests take a long time. Too short
2: Don't use channels from the function. Return value,ok, and make ok
false when a message should not be sent. Wrap that function in an
outer function, which does the channel operations. Only unit-test the
inner function. This would work in this particular simplistic case,
but not in all cases. One of the nice things about channels is that
they allow us to send messages without having to return from a
function. An illustration of this is the lexical scanner designed by
Rob Pike:
http://cuddle.googlecode.com/hg/talk/lex.html#slide-18
So I don't like 2.
3: Always return a value on the channel, but sometimes it's invalid.
I don't like this because it assumes I can come up with an invalid
value. What if all possible values of the message are legitimate?
Introduce a new field to the message struct, called "ok", that is
false don't want to send a message? Seems wasteful.
4: Introduce a new channel that exists only to signal to a testing
function that we're not going to be sending a message. The testing
function multiplexes on bChan and noopChan. This would work, but it
seems ugly to add a new channel only for testing. I also am not sure
it would work for all algorithms, though I'm having trouble coming up
with a specific example for which it wouldn't work.
So, has anyone else run into this? Any suggestions?