DEV Community πŸ‘©β€πŸ’»πŸ‘¨β€πŸ’»

Douglas Hubert
Douglas Hubert

Posted on

Mocking standard library functions

Hi! First time writing a post here. Hope you enjoy!

Introduction

It's quite common the needing to test some function from a standard library package. Today, I want to show you guys one simple way for testing some troublesome functions, such as time.Now, which I'm gonna use for this tutorial.

Hum, go on...

There are a few ways of doing that, one of them is injecting your target function (the one you want to mock, the time.Now in my case) in the function you are testing, just pass it as a parameter.

package main

import (
    "fmt"
    "time"
)

func WhatTimeIsIt(nowFunc func() time.Time) {
    fmt.Printf("The time is now: %v\n", nowFunc())
}

func main() {
    WhatTimeIsIt(time.Now)

    // So, when testing you could do something like this
    mockedNowFunc := func() time.Time {
        // I can control the time with one line of code, mwhahaha
        return time.Date(2050, time.January, 01, 15, 06, 54, 0, &time.Location{})
    }
    WhatTimeIsIt(mockedNowFunc)
}

// outputs
// The time is now: 2020-06-16 21:44:35.722303193 -0300 -03 m=+0.000060729
// The time is now: 2050-01-01 15:06:54 +0000 UTC

The problem with that is you need to pass the original Now function every time you want to know WhatTimeIsIt in production code :/

Of course this won't cause you a production bug, because Go Compiler is awesome and would warn you if you
forget a parameter when calling the function, but, it's still kind of annoying injecting it every time. It should have a way to avoid this...

YES, my young apprendice, there is. That's why I'm writing this post

It's very simple, actually, you just need to wrap your original function inside a global variable within your package... Wait, what?!

I'm gonna show you and you'll probably think: Why the hell did he write this post, then? Β―_(ツ)_/Β― Everyone needs to start somewhere, right?!

package main

import (
    "fmt"
    "time"
)

func WhatTimeIsIt() {
    fmt.Printf("The time is now: %v\n", nowFunc())
}

var nowFunc = func() time.Time {
    return time.Now()
}

func main() {
    WhatTimeIsIt()

    // So, when testing you could do something like this
    nowFunc = func() time.Time { // note that we just overwrote the global var nowFunc
        // I can control the time with one line of code, mwhahaha
        return time.Date(2050, time.January, 01, 15, 06, 54, 0, &time.Location{})
    }
    WhatTimeIsIt()
}

// output still the same
// The time is now: 2020-06-16 21:44:35.722303193 -0300 -03 m=+0.000060729
// The time is now: 2050-01-01 15:06:54 +0000 UTC

Now you don't need to pass the function every time, and when you need to mock the function for testing, or whatever reason, you can just overwrite the nowFunc var.

It's a nice tool for having in your toolkit. Probably, there are some other ways of doing that, but that's it for today.

Feel free to comment and thanks for reading!

Top comments (2)

Collapse
 
rpagliuca profile image
rpagliuca

Awesome, thanks for sharing. This pattern is indeed very useful in countless situations.

Collapse
 
primogf profile image
Marcio Zacarias

congratulations for the excellent post. Thanks, Douglas, for posting this!

50 CLI Tools You Can't Live Without

The top 50 must-have CLI tools, including some scripts to help you automate the installation and updating of these tools on various systems/distros.