How to mock functions in Go ?

2,439 views
Skip to first unread message

Nitish Saboo

unread,
Aug 1, 2019, 8:09:54 AM8/1/19
to golang-nuts
Hi,

How can we mock a function in Go like os.Hostname() or os.Getwd() ?
Any framework or Library that we can use for mocking the function calls ?

Thanks,
Nitish

Kyle Butz

unread,
Aug 1, 2019, 9:46:23 AM8/1/19
to golang-nuts
You'll want to look into gomock and mockgen.  To get started mocking, you'll need an interface, so maybe writing a receiver function encapsulating os.Hostname(), then consuming that with an interface.

type OsClient struct {}

func
(o *OsClient) GetOSHostname() (string, error) { return os.Hostname() }

type
HostnameGetter interface { GetOSHostname() (string, error) }

Gomock repo w/ documentation: https://github.com/golang/mock

Nice intros to gomock:

Note to the mods: I accidentally posted this from the wrong, unverified account first
Message has been deleted

Robert Engels

unread,
Aug 1, 2019, 10:29:30 AM8/1/19
to kyle...@sezzle.com, golang-nuts
That brings up an interesting idea. A ‘package replace’ during compile. So you request that the os package is replaced by osmock etc. 

This would allow easy general reuse mocking frameworks without changing the stdlib and all user code. 

On Aug 1, 2019, at 8:38 AM, kyle...@sezzle.com wrote:

You'll want to check out `gomock` and `mockgen`. You'll need to write an interface to be able to mock, so you may need to write a receiver struct to encapsulate os.Hostname() or whatever you need to mock, then mock your HostnameGetter (surely a better name than that), maybe?

type OsClient struct {}

(o *OsClient) GetHostname() (string, error) { return os.Hostname() }

type
HostnameGetter interface { GetHostname() (string, error)}



Here is repo w/ documentation: https://github.com/golang/mock

And here are a couple other nice introductions:
On Thursday, August 1, 2019 at 7:09:54 AM UTC-5, Nitish Saboo wrote:

--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/bceaef08-4ee2-4f57-8c0d-5fee59e8cfd7%40googlegroups.com.

Shulhan

unread,
Aug 1, 2019, 10:49:24 AM8/1/19
to Nitish Saboo, golang-nuts
I usually does something like these,

var getHostname = os.Hostname

and in test file,

getHostname = func() {
return "something", nil // or a variable
}

--
{ "github":"github.com/shuLhan", "site":"kilabit.info" }

Edward Muller

unread,
Aug 1, 2019, 12:14:33 PM8/1/19
to nitish....@gmail.com, golang-nuts
This can also be done without an interface: https://play.golang.org/p/fgRX2nXIxn0

In ^ example “Thing” would be the type you are working on, with functional options (https://dave.cheney.net/2014/10/17/functional-options-for-friendly-apis), and defaults to using os.Hostname.

PS: Use whichever method works for you, this is just my opinions.

--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.

Andrey Tcherepanov

unread,
Aug 4, 2019, 1:35:44 AM8/4/19
to golang-nuts
what if you need only a subpart of the package to be mocked ?


On Thursday, August 1, 2019 at 5:29:30 PM UTC+3, Robert Engels wrote:
That brings up an interesting idea. A ‘package replace’ during compile. So you request that the os package is replaced by osmock etc. 

This would allow easy general reuse mocking frameworks without changing the stdlib and all user code. 

On Aug 1, 2019, at 8:38 AM, kyle...@sezzle.com wrote:

You'll want to check out `gomock` and `mockgen`. You'll need to write an interface to be able to mock, so you may need to write a receiver struct to encapsulate os.Hostname() or whatever you need to mock, then mock your HostnameGetter (surely a better name than that), maybe?

type OsClient struct {}

(o *OsClient) GetHostname() (string, error) { return os.Hostname() }

type
HostnameGetter interface { GetHostname() (string, error)}



Here is repo w/ documentation: https://github.com/golang/mock

And here are a couple other nice introductions:

On Thursday, August 1, 2019 at 7:09:54 AM UTC-5, Nitish Saboo wrote:
Hi,

How can we mock a function in Go like os.Hostname() or os.Getwd() ?
Any framework or Library that we can use for mocking the function calls ?

Thanks,
Nitish

--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golan...@googlegroups.com.

Robert Engels

unread,
Aug 4, 2019, 7:23:25 AM8/4/19
to Andrey Tcherepanov, golang-nuts
In an ideal case got exported functions it would defer to the original. 

Still I think the package might have too many responsibilities if that’s the case. 
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/1e62434b-d7b6-403e-87a8-d0edf42d3dc0%40googlegroups.com.

Robert Engels

unread,
Aug 4, 2019, 7:29:02 AM8/4/19
to Andrey Tcherepanov, golang-nuts
Similarly the fact that you need to mock just os.Hostname and you know that means you are probably testing implementation details which I wouldn’t do. As I’ve pointed out before most of the Go stdlib tests are black box in a different test package. I think you’ll create better more resilient tests that way. 

Wojciech S. Czarnecki

unread,
Aug 4, 2019, 8:07:32 AM8/4/19
to golan...@googlegroups.com, xnow4f...@sneakemail.com
On Sat, 3 Aug 2019 22:35:44 -0700 (PDT)
Andrey Tcherepanov <xnow4f...@sneakemail.com> wrote:

> what if you need only a subpart of the package to be mocked ?

import smthorig "smth"

type smthT struct{
//functions
f1 func signature
f2 func signature
}

// functions
func mof1 (in...) (out...) { /* here your mock goes */ }
func mof2 (in...) (out...) { return smthorig(in...) }

var smth = smthT{ f1: mof1, f2: mof2 }

// methods
func (o smth) m1 (in...) (out...) { /* here your mock goes */ }
func (o smth) m2 (in...) (out...) { return smthorig(in...) }

Types are aliased
interfaces should be used from smthorig intact

Beware of packages that have init and keep state.

Hope this helps.

--
Wojciech S. Czarnecki
<< ^oo^ >> OHIR-RIPE

Nitish Saboo

unread,
Sep 20, 2019, 6:22:12 AM9/20/19
to Shulhan, golang-nuts
 I have a function in Go that I want to unit test but that function contains os.Hostname().Hence i thought of mocking os.Hostname.

Example:

func F(){
hostname, _ := os.Hostname()
}

I tried something like this:

var osHostname = os.Hostname

func TestF(t *testing.T) {
expected := "testing"
defer func() { osHostname = os.Hostname }()
osHostname = func()(string, error) { return "testing", nil }
actual := F()
assert.Equal(t,expected,actual)
}

But this doesn't work.Can someone please point me in the right direction?

Thanks,
Nitish

Mhd Shulhan

unread,
Sep 20, 2019, 6:37:52 AM9/20/19
to Nitish Saboo, golang-nuts


> On 20 Sep 2019, at 17.21, Nitish Saboo <nitish....@gmail.com> wrote:
>
> I have a function in Go that I want to unit test but that function contains os.Hostname().Hence i thought of mocking os.Hostname.
>
> Example:
>
> func F(){
> hostname, _ := os.Hostname()
> }
>
> I tried something like this:
>
> var osHostname = os.Hostname
>
> func TestF(t *testing.T) {
> expected := "testing"
> defer func() { osHostname = os.Hostname }()
> osHostname = func()(string, error) { return "testing", nil }
> actual := F()
> assert.Equal(t,expected,actual)
> }
>
> But this doesn't work.Can someone please point me in the right direction?

I have two notes about your code.
First, F() should have return values but its not.
Second, F() should use osHostname not os.Hostname.

The following pseudocode should works,

```
var osHostname = os.Hostname

func F() (string, error) {
return osHostname()
}

func TestF(t *testing.T) {
orgGetHostname := osHostname
osHostname = func() (string, error) {
return "testing", nil
}

defer func() {
osHostname = orgGetHostname
}()

exp := "testing"
got, _ := F()

if exp != got {
t.Fatalf("got %s, want %s", got, exp)
}
}
```

Nitish Saboo

unread,
Sep 20, 2019, 7:58:49 AM9/20/19
to Mhd Shulhan, golang-nuts
Tried running you code, it's failing:

--- FAIL: TestF (0.00s)
    test.go:43: got ubuntu, want testing
FAIL

Mhd Shulhan

unread,
Sep 20, 2019, 8:50:39 AM9/20/19
to Nitish Saboo, golang-nuts


On Fri, 20 Sep 2019, 18:58 Nitish Saboo, <nitish....@gmail.com> wrote:
Tried running you code, it's failing:

--- FAIL: TestF (0.00s)
    test.go:43: got ubuntu, want testing
FAIL


If that's the case I think you should learn about programming in general first, before learning how to mock a function.  I believe there are many online tutorial in the web.

Sam Whited

unread,
Sep 20, 2019, 10:17:16 AM9/20/19
to golan...@googlegroups.com
On Thu, Aug 1, 2019, at 12:09, Nitish Saboo wrote:
> How can we mock a function in Go like os.Hostname() or os.Getwd()
> ? Any framework or Library that we can use for mocking the
> function calls ?

I don't like to think about "mocking" in Go, but I can't provide an
alternative term so maybe this is overly pedantic (or maybe one
isn't needed).

For something like this, I prefer to avoid global state like others have
suggested: eventually, you'll run a test in parallel, try to reuse that
state, etc and it will lead to race conditions. You may think you won't
now, but it's not a problem until it's a problem so you might as well
avoid it. Instead, I'd just pass in a hostname to use. If this isn't
something that makes sense in the public API, put it in an internal
function and test that:

// Foo is a public API that needs a hostname.
func Foo() {
hostname, err := os.Hostname()
// error handling

foo(hostname)
}

// test this
func foo(hostname string) {
// …
}

I also prefer to only test published methods using a _testing package,
and this flies in the face of that advice a bit but in a situation like
this I think it's by far the simplest solution that won't lead to
headaches later.

—Sam
Reply all
Reply to author
Forward
0 new messages