Let's say I have a function if that is executed asynchronous as a go routine:
func f(wg *sync.WaitGroup){
defer wg.Done()
// Do sth
}
main(){
var wg sync.WaitGroup
wg.Add(1)
go f(&wg)
wg.Wait() // Wait until f is done
// ...
}
How would I create a unit test for f that makes sure wg.Done() is called?
One option is to call wg.Done() in the test directly after the f is called. If f fails to call wg.Done() the test will panic which is not nice.
Another option would be to create an interface for sync.WaitGroup but that seems a bit weird.
How would I create a unit test for f that makes sure wg.Done() is called?
Something like this:
func TestF(t *testing.T) {
wg := &sync.WaitGroup{}
wg.Add(1)
// run the task asynchronously
go f(wg)
// wait for the WaitGroup to be done, or timeout
select {
case <-wrapWait(wg):
// all good
case <-time.NewTimer(500 * time.Millisecond).C:
t.Fail()
}
}
// helper function to allow using WaitGroup in a select
func wrapWait(wg *sync.WaitGroup) <-chan struct{} {
out := make(chan struct{})
go func() {
wg.Wait()
out <- struct{}{}
}()
return out
}
You don't inspect the WaitGroup directly, which you can't do anyway. Instead you assert that the function behaves as expected, given the expected input.
In this case, the expected input is the WaitGroup argument and the expected behavior is that wg.Done() gets called eventually. What does that mean, in practice? It means that if the function is successful a WaitGroup with count 1 will reach 0 and allow wg.Wait() to proceed.
The statement defer wg.Done() at the beginning of f already makes sure that the test is resilient to errors or crashes. The addition of a timeout is simply to make sure the test will complete within a reasonable time, i.e. that it doesn't stall your test suite for too long. Personally, I prefer using explicit timeouts, either with timers or with contexts, to 1) avoid problems if someone forgets to set timeouts at the CI level, 2) make the time ceiling available to anyone who checks out the repo and runs the test suite, i.e. avoid dependencies on IDE configs or whatnot.
Related
I am testing a coroutine that blocks. Here is my production code:
interface Incrementer {
fun inc()
}
class MyViewModel : Incrementer, CoroutineScope {
override val coroutineContext: CoroutineContext
get() = Dispatchers.IO
private val _number = MutableStateFlow(0)
fun getNumber(): StateFlow<Int> = _number.asStateFlow()
override fun inc() {
launch(coroutineContext) {
delay(100)
_number.tryEmit(1)
}
}
}
And my test:
class IncTest {
#BeforeEach
fun setup() {
Dispatchers.setMain(StandardTestDispatcher())
}
#AfterEach
fun teardown() {
Dispatchers.resetMain()
}
#Test
fun incrementOnce() = runTest {
val viewModel = MyViewModel()
val results = mutableListOf<Int>()
val resultJob = viewModel.getNumber()
.onEach(results::add)
.launchIn(CoroutineScope(UnconfinedTestDispatcher(testScheduler)))
launch(StandardTestDispatcher(testScheduler)) {
viewModel.inc()
}.join()
assertEquals(listOf(0, 1), results)
resultJob.cancel()
}
}
How would I go about testing my inc() function? (The interface is carved in stone, so I can't turn inc() into a suspend function.)
There are two problems here:
You want to wait for the work done in the coroutine that viewModel.inc() launches internally.
Ideally, the 100ms delay should be fast-forwarded during tests so that it doesn't actually take 100ms to execute.
Let's start with problem #2 first: for this, you need to be able to modify MyViewModel (but not inc), and change the class so that instead of using a hardcoded Dispatchers.IO, it receives a CoroutineContext as a parameter. With this, you could pass in a TestDispatcher in tests, which would use virtual time to fast-forward the delay. You can see this pattern described in the Injecting TestDispatchers section of the Android docs.
class MyViewModel(coroutineContext: CoroutineContext) : Incrementer {
private val scope = CoroutineScope(coroutineContext)
private val _number = MutableStateFlow(0)
fun getNumber(): StateFlow<Int> = _number.asStateFlow()
override fun inc() {
scope.launch {
delay(100)
_number.tryEmit(1)
}
}
}
Here, I've also done some minor cleanup:
Made MyViewModel contain a CoroutineScope instead of implementing the interface, which is an officially recommended practice
Removed the coroutineContext parameter passed to launch, as it doesn't do anything in this case - the same context is in the scope anyway, so it'll already be used
For problem #1, waiting for work to complete, you have a few options:
If you've passed in a TestDispatcher, you can manually advance the coroutine created inside inc using testing methods like advanceUntilIdle. This is not ideal, because you're relying on implementation details a lot, and it's something you couldn't do in production. But it'll work if you can't use the nicer solution below.
viewModel.inc()
advanceUntilIdle() // Returns when all pending coroutines are done
The proper solution would be for inc to let its callers know when it's done performing its work. You could make it a suspending method instead of launching a new coroutine internally, but you stated that you can't modify the method to make it suspending. An alternative - if you're able to make this change - would be to create the new coroutine in inc using the async builder, returning the Deferred object that that creates, and then await()-ing at the call site.
override fun inc(): Deferred<Unit> {
scope.async {
delay(100)
_number.tryEmit(1)
}
}
// In the test...
viewModel.inc().await()
If you're not able to modify either the method or the class, there's no way to avoid the delay() call causing a real 100ms delay. In this case, you can force your test to wait for that amount of time before proceeding. A regular delay() within runTest would be fast-forwarded thanks to it using a TestDispatcher for the coroutine it creates, but you can get away with one of these solutions:
// delay() on a different dispatcher
viewModel.inc()
withContext(Dispatchers.Default) { delay(100) }
// Use blocking sleep
viewModel.inc()
Thread.sleep(100)
For some final notes about the test code:
Since you're doing Dispatchers.setMain, you don't need to pass in testScheduler into the TestDispatchers you create. They'll grab the scheduler from Main automatically if they find a TestDispatcher there, as described in its docs.
Instead of creating a new scope to pass in to launchIn, you could simply pass in this, the receiver of runTest, which points to a TestScope.
Questions
I would like to know the use case of t.Cleanup introduced in Go1.14. What is the convenience of t.Cleanup as compared to using defer?
https://golang.org/pkg/testing/#T.Cleanup.
Sample
For example, let's say we create a temporary directory, and when we test it, we want to delete the temporary directory we created.
t.Cleanup can be used to write a test as follows, but it also works as defer os.RemoveAll(tempDir).
package mypkg
import (
"io/ioutil"
"os"
"testing"
)
func TestDirwalk(t *testing.T) {
tempDir, err := ioutil.TempDir(".", "temp")
if err != nil {
t.Errorf("create tempDir: %v", err)
}
t.Cleanup(func() { os.RemoveAll(tempDir) })
// something...
}
Cleanup functions are also called if your test panics, so in your case both would work.
The advantage of using T.Cleanup() becomes clear if your test calls other functions, passing testing.T along. Obviously using defer in those functions would be executed before those functions return, but if you register cleanup functions using T.Cleanup(), then they will be called only at the end of your test.
Think of T.Cleanup() as an "improved" and extended version of defer. It also documents that the passed functions are for cleanup purposes.
t.Cleanup is useful for cleaning up resources allocated by a helper function when the test does not care about the resource itself.
Example
Consider testing a service layer. The service uses a *sql.DB but does not create it itself.
package testutils
import (
"testing"
"my/db"
"my/domain"
)
func NewTestSubject(t *testing.T) *domain.Service {
t.Helper()
sqldb := newDatabase(t)
s, _ := domain.NewService(sqldb)
return s
}
func newDatabase(t *testing.T) *sql.DB {
t.Helper()
d, _ := db.Create()
t.Cleanup(func() {
d.Close()
})
}
Without t.Cleanup newTestSubject would have to return (*domain.Service, *sql.DB), leaking details about domain.Service's construction.
Besides what others have pointed out, t.Cleanup() is also useful when dealing with parallel subtests, where the cleanup should only run after all subtests have completed. Consider
func TestSomething(t *testing.T){
setup()
defer cleanup()
t.Run("parallel subtest 1", func(t *testing.T){
t.Parallel()
(...)
})
t.Run("parallel subtest 2", func(t *testing.T){
t.Parallel()
(...)
})
}
which doesn't work because the test function will return while the the subtests are still running, causing the resources required by the subtests to get wiped out by defer cleanup().
Before t.Cleanup() a way to solve this was to wrap the subtests on another test
func TestSomething(t *testing.T){
setup()
defer cleanup()
t.Run("parallel tests", func(t *testing.T){
t.Run("subtest 1", func(t *testing.T){
t.Parallel()
(...)
})
t.Run("subtest 2", func(t *testing.T){
t.Parallel()
(...)
})
})
}
which looks ok, but with t.Cleanup() it gets way better
func TestSomething(t *testing.T){
setup()
t.Cleanup(cleanup)
t.Run("parallel subtest 1", func(t *testing.T){
t.Parallel()
(...)
})
t.Run("parallel subtest 2", func(t *testing.T){
t.Parallel()
(...)
})
}
I am implementing a API in Golang. I have a endpoint where I am calling a method with parameters of other package. Now I need to check that, that method has been called in the request.
Below is the small similar scenario what I am doing and what I am expecting.
My handler
package myPackage
import (
"log"
"github.com/myrepo/notifier" // my another package
)
func MyHandler(writer http.ResponseWriter, request *http.Request) {
// ...
// ...
notifier.Notify(4, "sdfsdf")
// ...
// ...
}
Testing handler
func TestMyHandler(t *testing.T) {
// Here I want to
req, _ := http.NewRequest("GET", "/myendpoint", nil)
// ... Want to test that notifier.Notify is called
// ...
}
In the TestMyHandler, I want to check that notifier.Notify has called.
Findings
I tried to understand the AssertNumberOfCalls, func (*Mock) Called, and func (*Mock) MethodCalled but I am not sure how to use them :(.
I am a newbie in Golang and really exicted to do that.
Please let me know if I missed anything or you may need more information for more understaing.
This is a good opportunity to use dependency injection and interfaces.
Namely, we need to extract the concept of a Notifier
(warning: code not tested directly)
type Notifier interface {
Notify(int, string)() error
}
Now to avoid any confusion with the notifier library, use a local alias.
import "github.com/myrepo/notifier" mynotifier
Then, because the library you're using exports it as a function, not within a struct, we'll need to make a struct that implements our interface
type myNotifier struct {}
func (mn *myNotifier) Notify(n int, message string) error {
return mynotifier.Notify(n, message)
}
Then you modify your function:
func MyHandler(writer http.ResponseWriter, request *http.Request, notifier Notifier) {
// ...
// ...
notifier.Notify(4, "sdfsdf")
// ...
// ...
}
Then in your test, you're now free to send in a spy Notifier
type spyNotifier struct {
called boolean
}
func (n *spyNotifier) Notify(n int, msg string) error {
n.called = true
return
}
Want to test that notifier.Notify is called.
No you don't. You are interested in that the handler does what it should do and this seems to consist of two things:
Return the right response (easy to test with a net/http/httptest.ResponseRecorder), and
Has some noticeable side effect, here issue some notification.
To test 2. you test that the notification was issued, not that some function was called.
Whatever notify.Notify results in (e.g. a database entry, a file, some HTTP call) should be tested. Formally this is no longer unit testing but testing for side effects is never strict unit testing.
What you can do: Wrap your handler logic into some object and observe that objects state. Ugly. Don't.
This approach is similar to Mathew's answer, but uses the mock package from testify instead. Here, you create a mock implementation of the Notifier, register the method call, and assert that the method has been called with the expected arguments.
Implementation
package handler
import (
"net/http"
"github.com/stretchr/testify/mock"
)
// the interface for the Notifier
type Notifier interface {
Notify(int, string) error
}
// the mock implementation of the interface above
type MockNotifier struct {
mock.Mock
}
// stub the notify method to ensure it can be expected later in the test
func (mockNotifier *MockNotifier) Notify(arg1 int, arg2 string) error {
args := mockNotifier.Called(arg1, arg2)
return args.Error(0)
}
// this handler which accepts a Notifier for dependency injection
type Handler struct {
notifier Notifier
}
// the MyHandler implementation which calls the notifier instance
func (h *Handler) MyHandler(writer http.ResponseWriter, request *http.Request) {
// this is what we want to test!
h.notifier.Notify(4, "sdfsdf")
}
Test
package handler_test
import (
"net/http"
"net/http/httptest"
"testing"
)
func TestMyHandler(t *testing.T) {
t.Parallel()
mockNotifier := MockNotifier{}
handler := Handler{ notifier: &mockNotifier }
// register the mock to expect a call to Notify with the given arguments and return nil as the error
mockNotifier.On("Notify", 4, "sdfsdf").Return(nil)
// setup the test arguments
request := httptest.NewRequest(http.MethodGet, "/someapi", nil)
writer := httptest.NewRecorder()
// call the handler
handler.MyHandler(writer, request)
// this is the important part!!
// this ensures that the mock Notify method was called with the correct arguments, otherwise the test will fail
mockNotifier.AssertExpectations(t)
}
At the moment I try to establish best practices for unit testing go handlers.
I need to mock dependencies, but therefore I have to be able to access / mock these dependencies.
There I some solutions I do not want to consider like for example global variables / application state. Or having all handlers as functions of a struct holding the dependencies member variables.
I was kind of satisfied with a solution where I injected the needed dependencies of a handler in the following way:
func helloHandler(db *DbService) http.HandlerFunc {
return func(w http.ResponseWriter, r *httpRequest) {
// handler code goes here
}
}
Then I am able to provide this handler for routing:
http.HandleFunc("/hello", helloHander(myDbService))
I can also test it easily:
helloHandler(myDbService)(req, respRecorder)
But when I use for example gorilla mux (what I haven't considered from the beginning) I get another problem.
Gorilla/mux adds extra semantics like filtering by Method (GET / POST), providing path parameters etc.
So I should need to test the resulting gorilla router.
But then I am again not able to inject my mock dependencies anymore.
When I get the router back everything (dependencies) is already set up.
But I also don't want to rebuild my Router in my tests because of DRY!
So for now I don't really have a good solution for the problem of conveniently setting up my routes but also being able to mock the dependencies.
Any idea?
I personally changed my Router function into a method with a Receiver, so I can create Mock receivers:
Production code:
func main(){
router := mux.NewRouter()
version.AddRouter(router, contextRoot)
someStruct := SomeStruct{SomeDependency{}}
router.HandleFunc(contextRoot, someStruct.HandleRequests).
Methods(http.MethodGet)
}
func (s SomeStruct) HandleRequests(writer http.ResponseWriter, reader *http.Request) {
...
}
func (s SomeDependency) SomeFunctionCalledFromHandler(...) SomeReturnStruct {
...
return SomeReturnStruct{}
}
Unit test:
type mockWriter struct {}
type someMockDependency struct {}
func Test_HandleRequest1(t *testing.T) {
someStructMockDeps := SomeStruct{someMockDependency{}}
someStructMockDeps.HandleRequests(mockWriter{}, &http.Request{URL: &url.URL{RawQuery:"http://dummy-query.com"}});
assert.Equal(...)
}
func (someMockDependency) SomeFunctionCalledFromHandler(...) SomeReturnStruct {
...
return SomeReturnStruct{}
}
Integration test:
func TestHandlerFuncIntg(t *testing.T) {
if testing.Short() {
println("skipping")
t.Skip()
}
req, err := http.NewRequest("GET", "/hello?param=value", nil)
if err != nil {
t.Fatal(err)
}
rr := httptest.NewRecorder()
someStructMockDeps := SomeStruct{someMockDependency{}}
handler := http.HandlerFunc(someStructMockDeps.HandleRequests)
handler.ServeHTTP(rr, req)
assert.Equal(t, http.StatusOK, rr.Code)
assert.Equal(t, "This is my result", rr.Body.String())
}
func (someMockDependency) SomeFunctionCalledFromHandler(...) SomeReturnStruct {
...
return SomeReturnStruct{}
}
SomeStruct declares the dependencies (as Dependency Injection) so they can be overridden for tests.
http://play.golang.org/p/vhaKi5uVmm
package main
import "fmt"
var battle = make(chan string)
func warrior(name string, done chan struct{}) {
select {
case opponent := <-battle:
fmt.Printf("%s beat %s\n", name, opponent)
case battle <- name:
// I lost :-(
}
done <- struct{}{}
}
func main() {
done := make(chan struct{})
langs := []string{"Go", "C", "C++", "Java", "Perl", "Python"}
for _, l := range langs { go warrior(l, done) }
for _ = range langs { <-done }
}
[1st Question]
done <- struct{}{}
How and Why do we need this weird-looking struct? Is it empty struct or anonymous struct? I googled it but couldn't find the right answer or documentation to explain about this.
The original source is from Andrew Gerrand's talk
http://nf.wh3rd.net/10things/#10
Here
make(chan struct{})
done is a channel of type struct{}
So I tried with
done <- struct{}
But it is not working. Why do I need an extra brackets for this line?
done <- struct{}{}
[2nd Question]
for _ = range langs { <-done }
Why do I need this line? I know that this line is necessary because without this line, no output. But Why and what does this line do? And what makes it necessary in this code? I know that <-done is to receive values from the channel done and discard the received values. But why do I need to do this?
Note that one interesting aspect of using struct{} for the type pushed to a channel (as opposed to int or bool), is that the size of an empty struct is... 0!
See the recent article "The empty struct" (March 2014) by Dave Cheney.
You can create as many struct{} as you want (struct{}{}) to push them to your channel: your memory won't be affected.
But you can use it for signaling between go routines, as illustrated in "Curious Channels".
finish := make(chan struct{})
As the behaviour of the close(finish) relies on signalling the close of the channel, not the value sent or received, declaring finish to be of type chan struct{} says that the channel contains no value; we’re only interested in its closed property.
And you retain all the other advantages linked to a struct:
you can define methods on it (that type can be a method receiver)
you can implement an interface (with said methods you just define on your empty struct)
as a singleton
in Go you can use an empty struct, and store all your data in global variables. There will only be one instance of the type, since all empty structs are interchangeable.
See for instance the global var errServerKeyExchange in the file where the empty struct rsaKeyAgreement is defined.
Composite literals
Composite literals construct values for structs, arrays, slices, and
maps and create a new value each time they are evaluated. They consist
of the type of the value followed by a brace-bound list of composite
elements. An element may be a single expression or a key-value pair.
struct{}{} is a composite literal of type struct{}, the type of the value followed by a brace-bound list of composite elements.
for _ = range langs { <-done } is waiting until all the goroutines for all the langs have sent done messages.
struct{} is a type (in particular, a structure with no members). If you have a type Foo, you can create a value of that type in an expression with Foo{field values, ...}. Putting this together, struct{}{} is a value of the type struct{}, which is what the channel expects.
The main function spawns warrior goroutines, which will write to the done channel when they have finished. The last for block reads from this channel, ensuring that main won't return until all the goroutines have finished. This is important because the program will exit when main completes, irrespective of whether there are other goroutines running.
Good questions,
The whole point of the struct channel in this scenario is simply to signal the completion that something useful has happened. The channel type doesn't really matter, he could have used an int or a bool to accomplish the same effect. What's important is that his code is executing in a synchronized fashion where he's doing the necessary bookkeeping to signal and move on at key points.
I agree the syntax of struct{}{} looks odd at first because in this example he is declaring a struct and creating it in-line hence the second set of brackets.
If you had a pre-existing object like:
type Book struct{
}
You could create it like so: b := Book{}, you only need one set of brackets because the Book struct has already been declared.
done channel is used to receive notifications from warrior method that indicates the worker is done processing. So the channel can be anything, for example:
func warrior(name string, done chan bool) {
select {
case opponent := <-battle:
fmt.Printf("%s beat %s\n", name, opponent)
case battle <- name:
// I lost :-(
}
done <- true
}
func main() {
done := make(chan bool)
langs := []string{"Go", "C", "C++", "Java", "Perl", "Python"}
for _, l := range langs { go warrior(l, done) }
for _ = range langs { <-done }
}
We declare done := make(chan bool) as a channel that receives bool value, and send true at the end of warrior instead. This works! You can also define the done channel to any other type, it won't matter.
1. So what is with the weird done <- struct{}{}?
It is just another type that will be passed to channel. This is an empty struct, if you are familiar with the following:
type User struct {
Name string
Email string
}
struct{} makes no difference except it contains no fields, and struct{}{} is just an instance out of it. The best feature is it does not cost memory space!
2. for loop usage
We create 6 goroutines to run in the background with this line:
for _, l := range langs { go warrior(l, done) }
We use the for _ = range langs { <-done }, because the main goroutine(where main function runs) does not wait for goroutins to finish.
If we does not include the last for line, chances are we see no outputs(because main goroutines quits before any child goroutines executes fmt.Printf code, and when main goroutine quits, all child goroutines will quit with it, and will not have any chance to run anyway).
So we wait for all goroutines to finish(it runs to the end, and send a message to the done channel), then exits. done channel here is a blocked channel, which means <-done will block here until a message is received from the channel.
We have 6 goroutines in the background, and use for loop, we wait until all goroutines send a message which means it finished running(because the done <-struct{}{} is at the the end of function).