Reputation: 41
I'm trying to mock net.Interface in Go, I use net.Interfaces() and I want to have a fixed return. But net.Interface is not an interface, so I can't mock it with gomock.
Maybe I'm wrong in the way I test.
Here is the method I want to test:
const InterfaceWlan = "wlan0"
const InterfaceEthernet = "eth0"
var netInterfaces = net.Interfaces
func GetIpAddress() (net.IP, error) {
// On récupère la liste des interfaces
ifaces, err := netInterfaces()
if err != nil {
return nil, err
}
// On parcours la liste des interfaces
for _, i := range ifaces {
// Seul l'interface Wlan0 ou Eth0 nous intéresse
if i.Name == InterfaceWlan || i.Name == InterfaceEthernet {
// On récupère les adresses IP associé (généralement IPv4 et IPv6)
addrs, err := i.Addrs()
// Some treatments on addrs...
}
}
return nil, errors.New("network: ip not found")
}
Here is the test I wrote for the moment
func TestGetIpAddress(t *testing.T) {
netInterfaces = func() ([]net.Interface, error) {
// I can create net.Interface{}, but I can't redefine
// method `Addrs` on net.Interface
}
address, err := GetIpAddress()
if err != nil {
t.Errorf("GetIpAddress: error = %v", err)
}
if address == nil {
t.Errorf("GetIpAddress: errror = address ip is nil")
}
}
Minimal reproductible example:
Upvotes: 1
Views: 2099
Reputation: 86
IMO. you can injection function net.Interface
And function getAddrs
for get []net.Addrs
into GetIpAddress
type NetworkHandler struct {
GetInterfaces func() ([]net.Interface,error)
GetAddrsFromInterface func(p net.Interface) ([]net.Addr,error)
}
func GetIpAddress(networkHandler NetworkHandler) (net.IP,error) {
// On récupère la liste des interfaces
ifaces, err := networkHandler.GetInterfaces()
if err != nil {
return nil, err
}
// On parcours la liste des interfaces
for _, i := range ifaces {
// Seul l'interface Wlan0 ou Eth0 nous intéresse
if i.Name == InterfaceWlan || i.Name == InterfaceEthernet {
// On récupère les adresses IP associé (généralement IPv4 et IPv6)
_, err := networkHandler.GetAddrsFromInterface(i)
// ex
if err != nil {
return nil, err
}
// Some treatments on addrs...
}
}
return nil, errors.New("network: ip not found")
}
ON Test code
func TestGetIpAddress(t *testing.T) {
t.Run("should return error when cannot get address from net.interface", func(t *testing.T) {
result, err := GetIpAddress(NetworkHandler{
GetInterfaces: mockGetInterfaces,
GetAddrsFromInterface: mockGetAddrs,
})
assert.Nil(t, result)
assert.Error(t, err)
assert.Equal(t, "cannot get addrs",err.Error())
})
}
func mockGetInterfaces() ([]net.Interface,error) {
return []net.Interface{
{Name: "wlan0"},
{Name: "eth0"}},nil
}
// stub behavior when calling net.Interface{}.Addrs()
func mockGetAddrs(i net.Interface) ([]net.Addr,error) {
return nil, errors.New("cannot get addrs")
}
on usage
func main() {
GetIpAddress(NetworkHandler{
GetInterfaces: net.Interfaces,
GetAddrsFromInterface: func(p net.Interface) ([]net.Addr, error) {
return p.Addrs()
},
})
}
Upvotes: 1
Reputation: 5187
You can use a method expression to bind the method to a variable of function type, in much the same way that you are already binding the net.Interfaces
function to a variable:
var (
netInterfaces = net.Interfaces
netInterfaceAddrs = (*net.Interface).Addrs
)
func GetIpAddress() (net.IP, error) {
…
// Get IPs (mock method Addrs ?)
addrs, err := netInterfaceAddrs(&i)
…
}
Then, in the test, you can update the binding in the same way:
func TestGetIpAddress(t *testing.T) {
…
netInterfaceAddrs = func(i *net.Interface) ([]net.Addr, error) {
return []net.Addr{}, nil
}
…
}
(https://play.golang.org/p/rqb0MDclTe2)
That said, I would recommend factoring out the mocked methods into a struct type instead of overwriting global variables. That allows the test to run in parallel, and also allows downstream users of your package to write their own tests without mutating global state.
// A NetEnumerator enumerates local IP addresses.
type NetEnumerator struct {
Interfaces func() ([]net.Interface, error)
InterfaceAddrs func(*net.Interface) ([]net.Addr, error)
}
// DefaultEnumerator returns a NetEnumerator that uses the default
// implementations from the net package.
func DefaultEnumerator() NetEnumerator {
return NetEnumerator{
Interfaces: net.Interfaces,
InterfaceAddrs: (*net.Interface).Addrs,
}
}
func GetIpAddress(e NetEnumerator) (net.IP, error) {
…
}
(https://play.golang.org/p/PLIXuOpH3ra)
Upvotes: 5