I am a bit lost when it comes to unit testing in Golang. When developing in Java and Javascript it is easy to mock out data and functions, make assertions and expectations such as making sure some function was called inside a function your testing. What are some things to make sure to test when unit testing Golang functions and what is a good strategy for mocking things out?
Ha! There are maaaany opinions in this thread, but I will add mine.<p>I'm not a fan of writing stupid mocks. That doesn't give me much confidence, and spending time on tests is mostly about building ones confidence. I very much prefer running the program end-to-end with defined inputs, and checking its outputs.<p>Whether that's database, file on disk, or REST api. I write python nosetests that prepare the setup, run golang program, run the checks, and tear down the setup. Sadly, normally it's hard to get golang code coverage for end-to-end tests (aka integration tests). This is what I usually do:<p><a href="https://blog.cloudflare.com/go-coverage-with-external-tests/" rel="nofollow">https://blog.cloudflare.com/go-coverage-with-external-tests/</a>
Take a look at the Golang source for some examples of table-driven tests. When you need mocking you can usually use interfaces and then define a struct in your test that satisfies the methods of the interface.<p>I'm currently co-authoring a book called Production Go which includes a chapter on testing. That chapter is available in the free sample: <a href="https://leanpub.com/productiongo/" rel="nofollow">https://leanpub.com/productiongo/</a> and might cover some of your questions.
We make use of <a href="https://github.com/onsi/ginkgo" rel="nofollow">https://github.com/onsi/ginkgo</a> and Gomega <a href="https://github.com/onsi/gomega" rel="nofollow">https://github.com/onsi/gomega</a> for BDD testing with more expressive matchers. You might be familiar with some of these concepts from Cucumber/Jasmine/Mocha.<p>They get significant use in Cloud Foundry and in the Kubernetes e2e tests.<p>I appreciate a lot of people like the simplicity of the standard test library and if you're starting with Go development, it's not a bad idea to get used to that before exploring external dependencies.<p>Caveat: While not necessarily a "maintainer", I have been doing a bunch of maintenance on these projects the past few weeks.<p>Edit: Also we use counterfeiter <a href="https://github.com/maxbrunsfeld/counterfeiter" rel="nofollow">https://github.com/maxbrunsfeld/counterfeiter</a> for fakes.
I like to use a pattern like this: given a resource defined as an interface:<p><pre><code> type Database interface {
Users() ([]*User, error)
}
</code></pre>
I like to create an explicitly-mocked version of it like this:<p><pre><code> type MockDatabase struct {
UsersStub func() ([]*User, error)
}
func (m MockDatabase) Users() ([]*User, error) {
if m.UsersStub == nil {
panic("MockDatabase.UsersStub is nil")
}
return (m.UsersStub)()
}
</code></pre>
Then you can create the resources you need, and define how they work, at the top of your test:<p><pre><code> func TestSomething(t *testing.T) {
db := MockDatabase{
UsersStub: func() ([]*User, error) {
...
}
}
// use db here
}
</code></pre>
This works well as long as the things you're testing take all the resources they need as parameters.
Design components based on interfaces. Use dependency injection, Testify [0] for asserts and mocks, and Mockery (based on Testify) [1] to generate mocks.<p>[0] <a href="https://github.com/stretchr/testify" rel="nofollow">https://github.com/stretchr/testify</a>
[1] <a href="https://github.com/vektra/mockery" rel="nofollow">https://github.com/vektra/mockery</a>
I found the talk "Advanced Testing With Go" to be illuminating.<p>Video: <a href="https://www.youtube.com/watch?v=yszygk1cpEc" rel="nofollow">https://www.youtube.com/watch?v=yszygk1cpEc</a><p>Slides: <a href="https://speakerdeck.com/mitchellh/advanced-testing-with-go" rel="nofollow">https://speakerdeck.com/mitchellh/advanced-testing-with-go</a>
Java and co can generate logic at runtime, go cannot so you have to manually write mocks, or "generate" them with a third party executable.<p>You cannot mock anything if you don't use go interfaces at first place, there is no inheritance in go so you can't swap types unless they are interfaces or convertible.<p>given an interface Fooer :<p><pre><code> type Fooer interface {
Do(int)int
}
</code></pre>
and a method to test<p><pre><code> func Accept(f Fooer){
f.Do(10)
}
</code></pre>
it's easy to write a mock<p><pre><code> type FooImpl struct {
T testing.T
}
func(f FooImpl)Do(i int)int{
f.T.Helper()
if i!=10{
f.T.Fail("i=10 expected")
}
return 0
}
// the test
func TestAccept(t testing.T){
Accept(FooImpl{T:t})
}
</code></pre>
Again, your methods have to accept interfaces at first place or you can't mock anything. With Go you'll often have to step back and do manually the things you took for granted in Java.<p>Only go maintainers can fix the "issue", by allowing the method definition on ad hoc structs, which is impossible right now.
Check out <a href="https://github.com/stretchr/testify" rel="nofollow">https://github.com/stretchr/testify</a> for more convenient assertions, as well as mocking support for implementing interfaces within your tests.<p>You cannot do ad-hoc mocking of arbitrary functions in Go. If you want to write component-level tests that mock out other components, then you need an interface boundry between those components. Or better yet, you just write tests at the network level, spinning up mock clients/servers on localhost.
If you're comfortable with this style of testing, you can do it in Go using interfaces and code generation.<p>Counterfeiter hooks into `go generate`: <a href="https://github.com/maxbrunsfeld/counterfeiter" rel="nofollow">https://github.com/maxbrunsfeld/counterfeiter</a><p>Here's an example project: <a href="https://github.com/andreasf/cf-mysql-plugin" rel="nofollow">https://github.com/andreasf/cf-mysql-plugin</a>
If you want to make testing a bit less verbose, you can look into testify (<a href="https://github.com/stretchr/testify" rel="nofollow">https://github.com/stretchr/testify</a>), it adds some helpful assertions (no more 'if err != nil ...').<p>If you are using interfaces with dependency injection, it is easy to mock the dependencies of whatever you are unit testing implementing the interfaces you have defined in your testing file.
A colleague of mine made a tutorial for GoMock, maybe its helpful:
<a href="https://blog.codecentric.de/en/2017/08/gomock-tutorial/" rel="nofollow">https://blog.codecentric.de/en/2017/08/gomock-tutorial/</a>
It's pretty much the same as in other languages, so don't over think it. The main difference for you will be Go is compiled, so you have to think slightly differently when writing tests.<p>Here are a few don'ts:<p>- don't take interfaces as arguments unless it really makes sense, even if it makes testing easier<p>- don't use one of the tiny test helper libraries that basically just writes if statements for you<p>- don't start writing tests until you've already figured out how to organize your logging and errors<p>One pattern I found helpful was to write an adapter that uses the testing.T.Print* functions as the destination for log.Print* statements in my regular code so that log messages aren't jumbled when running tests in parallel.
1. Interfaces allow swapping out objects.<p>2. Mocking is a bad idea even in languages that support it well.<p>A better approach, when you do need to deal with fakes, is verified fakes: write two versions of an interface, and a test suite for that interface to ensure the fake acts the same as the real thing.<p>More here: <a href="https://codewithoutrules.com/2016/07/31/verified-fakes/" rel="nofollow">https://codewithoutrules.com/2016/07/31/verified-fakes/</a>
I have recently created a small project called rocket (<a href="https://github.com/joernlenoch/rocket" rel="nofollow">https://github.com/joernlenoch/rocket</a>) to provide myself with a simple dependency injection module for exactly this reason.<p>This is rather a starting point than a solution for your problem. But combined with some "fakers" and supplied testing frameworks this might remove some of the boilerplate.
The age of Docker means IMO you should avoid mocks. There's no reason not to have a full integration test against your real API especially because you can compile and run test binaries independently.
There's a good JustForFunc episode on testing. <a href="https://www.youtube.com/watch?v=hVFEV-ieeew" rel="nofollow">https://www.youtube.com/watch?v=hVFEV-ieeew</a>