简体   繁体   中英

How to use a struct/interface to mock dependency for testing

I'm new to go here... my objective is to unit test that a status is being updated in my ready(). I've been looking at https://engineering.aircto.com/writing-testable-code-in-golang/ and trying to figure out how to adapt what they're doing to my use case, filling in gaps of golang knowledge where I can.

I'm getting the error cannot use fakeSession (type *FakeSession) as type *discordgo.Session in argument to ready but I'm not sure why I'm getting this error.

main.go

import (
    "fmt"
    "os"
    "os/signal"
    "syscall"

    "github.com/bwmarrin/discordgo"
)

var (
    // bot token used for this bot when connecting
    token    = os.Getenv("DISCORD_BOT_TOKEN")
    status   = os.Getenv("BOT_STATUS")
)

func main() {
    // initiate Discord bot

    // Register ready as a callback for the ready events.
    discordConnection.AddHandler(ready)

    // running the app, waiting to receive a close signal
}

// This function will be called (due to AddHandler above) when the bot receives
// the "ready" event from Discord.
func ready(session *discordgo.Session, event *discordgo.Ready) {

    // Set the playing status.
    session.UpdateStatus(0, status)
}

main_test.go

type FakeSession struct {
    status  string
    idle    int
}

func (f *FakeSession) UpdateStatus(idle int, game string) error {
    f.idle, f.status = idle, game
    return nil
}

func TestStatusIsUpdated(t *testing.T) {
    readyDependency := &discordgo.Ready{}
    fakeSession := &FakeSession{}

    ready(fakeSession, readyDependency)

    // @todo assert that idle/game status were set to correct values
}

As @Andrew pointed out discordgo.Session is a go struct (from the docs link you posted type Session struct { )

structs are Concrete types in go and are unable to be substituted. The only argument go compiler will allow for ready is a pointer to a session.

To break this dependency you can create a custom interface owned and controlled by your project using the methods that you need. This will allow you to create and call ready with a fake structure for your tests.

Sometimes 3rd party libraries already have interfaces so it's usually worth-while to scan their godoc to see which interfaces are available before creating your own.


But if you have to create your own for testing (and I find myself regularly having to do this), it might look like:

type StatusUpdater interface {
   UpdateStatus(int, string)
}

// This function will be called (due to AddHandler above) when the bot receives
// the "ready" event from Discord.
func ready(s StatusUpdater, event *discordgo.Ready) {

    // Set the playing status.
    s.UpdateStatus(0, status)
}

Now the dependency on discordgo.Session has been broken, and your test code can call ready function with its fake session, and then make assertions on it!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM