Live data from Hacker News

Ask HN: How to approach testing in Golang?

news.ycombinator.com

21–30 of 37 posts

Re: Ask HN: How to approach testing in Golang?

#21
post #2

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. I'm currently co-authoring a book called Production Go which includes a chapter on testing. That chapter is available in the free sample: https://leanpub.com/productiongo/ and might cover some of your questions…

[deleted]

Re: Ask HN: How to approach testing in Golang?

#23
post #17
post #16

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.

Not an apologist for unit tests, but I'll bite here: unit tests are still valuable when you have the capacity to run ful integration tests. A unit test can run really fast, for one, it can cover future use cases that don't get covered in integration, it edge cases you don't expect to hit, but are still important to cover in case something happens. I'm sure there are more, good reasons to write unit tests and integrat…

Unit tests run much faster and are more focused - relying too much on integration tests to catch bugs is a massive maintenance smell, as you will waste an enormous amount of time trying to figure out the root cause of a failing test.

Re: Ask HN: How to approach testing in Golang?

#24
Ha! There are maaaany opinions in this thread, but I will add mine.

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.

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:

https://blog.cloudflare.com/go-coverage-with-external-tests/

Re: Ask HN: How to approach testing in Golang?

#25

Also, consider what you are really testing with mocks. Your implementation, or your mock? You can test without mocks..,

> You can test without mocks...

There are some things that are implausible to test without injecting not-production-code. Many of these look like errors (bad inputs, expired sessions, etc.) that should be intelligently handled.

Re: Ask HN: How to approach testing in Golang?

#26
I like to use a pattern like this: given a resource defined as an interface:

    type Database interface {
        Users() ([]*User, error)
    }
I like to create an explicitly-mocked version of it like this:

    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)()
    }
Then you can create the resources you need, and define how they work, at the top of your test:

    func TestSomething(t *testing.T) {
        db := MockDatabase{
            UsersStub: func() ([]*User, error) {
                ...
            }
        }

        // use db here
    }
This works well as long as the things you're testing take all the resources they need as parameters.

Re: Ask HN: How to approach testing in Golang?

#27
post #26

I like to use a pattern like this: given a resource defined as an interface: type Database interface { Users() ([]*User, error) } I like to create an explicitly-mocked version of it like this: 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)() } Then you can create the r…

This is pretty much it, mocking is quite simple and straightforward. It's tedious but wrapping access to external resources (eg: cache, HTTP, database) is necessary for a sane Go-based application.

Re: Ask HN: How to approach testing in Golang?

#28
post #26

I like to use a pattern like this: given a resource defined as an interface: type Database interface { Users() ([]*User, error) } I like to create an explicitly-mocked version of it like this: 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)() } Then you can create the r…

I also use that same pattern. I'm very tempted to write a tool to be called by `go generate` to automatically create that kind of mock.

Re: Ask HN: How to approach testing in Golang?

#29
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.

Here are a few don'ts:

- don't take interfaces as arguments unless it really makes sense, even if it makes testing easier

- don't use one of the tiny test helper libraries that basically just writes if statements for you

- don't start writing tests until you've already figured out how to organize your logging and errors

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.

Re: Ask HN: How to approach testing in Golang?

#30
post #26

I like to use a pattern like this: given a resource defined as an interface: type Database interface { Users() ([]*User, error) } I like to create an explicitly-mocked version of it like this: 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)() } Then you can create the r…

I also use that same pattern. I'm very tempted to write a tool to be called by `go generate` to automatically create that kind of mock.

Please don't. I mean, do, whatever makes you happy. There's at least 3 of these flying around in various stages of abandonment.

Here's one:

https://github.com/vektra/mockery

Post reply on HN