I don’t understand why this uses goroutines, timeouts and channels to run something which hasn’t got any concurrency.
What is the advantage over a serial implementation?
Hey, OP here. First off, keep in mind whenever a technique is blogged it has to be simplified or the blog post would be unreadably long. So, yes, this technique is used in real code for managing a multi-goroutine system. But in the blog example it’s just one, which makes it look like overkill. It’s a simple technique that really gives you a lot of growth and power as the code complexity increases.
Second, I am not using timeouts and I am only using a single goroutine to run assertions in sync with the executing code. IMO this is the actual power of the technique: the fact that the assertion code is forced to sync with the executing code so that every call to the fake is accounted for and correct. It’s quite strict.
I would like to see an example of a serial implementation. My guess is you would store the call and then have a method to retrieve it? This becomes difficult when one method call to the unit under test executes three calls to the same method on the fake. You need to start storing arrays of calls and be able to inspect an array of responses. IMO channels are actually simpler!
Feel free to continue the discussion here or on the comments section of the site. I’d love more feedback on the technique.
I’m not getting a ton of sleep at the moment; if I’m not totally coherent please let me know.
The serial implementation would indeed have to store the call.
I’d envisage something like this.
Of course, I’m bending the interface to fit the test, and moving the complexity there - which isn’t going to be everyones preferred way to test.
I have no idea what problems the OP is trying to solve, but you will on occasion see goroutines/channels used as an implementation detail in order to expose an API that is safe to call from multiple goroutines simultaneously. i.e., “thread safe.” I have no idea whether this is what the OP was trying to do or not.
(comment removed, I replied to the wrong thread, sorry!)
I think you meant to respond to /u/danielrheath
Yeah sorry my bad. First comment :)
[Comment removed by author]
bingo! But a queue of size 1. Although you could certainly switch to buffered channels and get a queue pretty easy if you don’t want to deal with backgrounding the assertions.
So, we create a FakeClient and give it the testing instance so it can make assertions. Then we inject it as the Grocery List’s store. This is called Setter Injection.
Why don’t you do this as part of construction? That would make it a lot less error prone.
list := New(client)
That works too! It depends on the interface you want to expose. In some cases I don’t want a user of my code to be able to change an internal dependency, so I use a private attribute and setter injection. But if it’s part of the public API then what you have suggested is probably better. It’s a subjective difference.