Reputation: 6602
Somewhat of a golang beginner, but I've worked with testing frameworks before. How do I go about mocking out and faking what a dependent method returns without injecting the dependency? The reason why I don't want to use dependency injection is because there are many external package methods that are being used and injecting all of the methods in the constructor is unwieldy.
I've searched for this online/stackoverflow and the solution is to always use dependency injection. Sometimes that is not a viable option.
Here's what I'm trying to do code-wise:
b/b_test.go
package b
func TestResults(t *testing.T) {
t.Run("Test", func(t *testing.T) {
b := NewB()
// How do I mock out and fake a.DoSomething() to be
// "complete" instead of whats in the code right now?
result = b.Results()
assert.Equal(t, "complete", result)
}
}
b/b.go
package b
import "a"
type B struct {}
func NewB() B {
return &B{}
}
func (b B) Results() {
return a.DoSomething()
}
a/a.go
package a
func DoSomething() {
return "done"
}
Thanks!
Upvotes: 23
Views: 14260
Reputation: 1885
One way to do so would be to create a variable with the function you want to call, so include the following in b/b.go:
var doSomething = a.DoSomething
func (b B) Results() {
return doSomething()
}
Now in b_test.go you can do this:
func TestPrintResults(t *testing.T) {
origDoSomething := doSomething
defer func() { doSomething = origDoSomething }
doSomething = func() {
// Insert fake implementation here
}
b := NewB()
result = b.Results()
assert.Equal(t, "complete", result)
}
Upvotes: 7
Reputation: 425
I'm not sure if I don't understand your objection to dependency injection, but interfaces can be used to make dependency injection relatively painless. In particular existing code does not need to be modified.
You could try aliasing the package name to a global variable that implements an interface that matches the external package's functions. This has the advantage of not requiring inline changes where package "a" is used.
The idea revolves around creating an interface for the functions you need from the external package, a pass-through implementation of that interface for the default behavior and a mock implementation for testing. At the beginning of a test just replace the global variable with the mock.
b/a_interface.go
package b
import (
aa "a" // alias the "a" package
)
// global variable that mimics the external package "a"
var a aType
// internal interface for `a` package functions (i.e. `DoSomething()`)
type aDoer interface {
DoSomething() string
}
// default implementation of the aDoer interface
type aType struct{}
func (aType) DoSomething() string {
// just pass-through to package "a"
return aa.DoSomething()
}
b/b.go - is unmodified other then removing the import:
package b
type B struct{}
func NewB() B {
return B{}
}
func (b B) Results() string{
// now `a` is a global variable not a package.
return a.DoSomething()
}
b/b_test.go
package b
import (
"testing"
"github.com/stretchr/testify/assert"
)
// mock implementation of aDoer interface
type aMock struct{}
func (aMock) DoSomething() string {
return "complete"
}
func TestResults(t *testing.T) {
a = aMock{} // <- replace the default with the mock
b := NewB()
result = b.Results()
assert.Equal(t, "complete", result)
}
It's a bit on the sneaky side, so you'll probably want to make clear comments about what's going on.
Upvotes: 4
Reputation: 8490
You can use conditional compilation with build tags
a/a.go
// +build !mock
package a
func DoSomething() {
return "done"
}
a/a_mock.go
// +build mock
package a
func DoSomething() { // Insert fake implementation here
return "complete"
}
$ go test -tags mock
Upvotes: 17