英文:
How to mock net.Interface
问题
我正在尝试在Go中模拟net.Interface,我使用net.Interfaces(),并且希望有一个固定的返回值。但是net.Interface不是一个接口,所以我无法使用gomock来模拟它。
也许我在测试的方式上有些错误。
这是我想要测试的方法:
const InterfaceWlan = "wlan0"
const InterfaceEthernet = "eth0"
var netInterfaces = net.Interfaces
func GetIpAddress() (net.IP, error) {
// 获取接口列表
ifaces, err := netInterfaces()
if err != nil {
return nil, err
}
// 遍历接口列表
for _, i := range ifaces {
// 只关注Wlan0或Eth0接口
if i.Name == InterfaceWlan || i.Name == InterfaceEthernet {
// 获取关联的IP地址(通常是IPv4和IPv6)
addrs, err := i.Addrs()
// 对addrs进行一些处理...
}
}
return nil, errors.New("network: ip not found")
}
这是我目前编写的测试:
func TestGetIpAddress(t *testing.T) {
netInterfaces = func() ([]net.Interface, error) {
// 我可以创建net.Interface{},但是我无法重新定义net.Interface上的`Addrs`方法
}
address, err := GetIpAddress()
if err != nil {
t.Errorf("GetIpAddress: error = %v", err)
}
if address == nil {
t.Errorf("GetIpAddress: errror = address ip is nil")
}
}
最小可复现示例:
英文:
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:
答案1
得分: 5
你可以使用方法表达式将方法绑定到函数类型的变量上,就像你已经将net.Interfaces
函数绑定到变量上一样:
var (
netInterfaces = net.Interfaces
netInterfaceAddrs = (*net.Interface).Addrs
)
func GetIpAddress() (net.IP, error) {
…
// 获取IP地址(模拟方法Addrs?)
addrs, err := netInterfaceAddrs(&i)
…
}
然后,在测试中,你可以以相同的方式更新绑定:
func TestGetIpAddress(t *testing.T) {
…
netInterfaceAddrs = func(i *net.Interface) ([]net.Addr, error) {
return []net.Addr{}, nil
}
…
}
(https://play.golang.org/p/rqb0MDclTe2)
话虽如此,我建议将模拟的方法提取到一个结构类型中,而不是覆盖全局变量。这样可以使测试并行运行,并且允许你包的下游用户编写自己的测试而不会改变全局状态。
// NetEnumerator 枚举本地IP地址。
type NetEnumerator struct {
Interfaces func() ([]net.Interface, error)
InterfaceAddrs func(*net.Interface) ([]net.Addr, error)
}
// DefaultEnumerator 返回一个使用net包中默认实现的NetEnumerator。
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)
英文:
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) {
…
}
答案2
得分: 1
IMO. 你可以将函数net.Interface
和函数getAddrs
注入到GetIpAddress
中,以获取[]net.Addrs
。
type NetworkHandler struct {
GetInterfaces func() ([]net.Interface, error)
GetAddrsFromInterface func(p net.Interface) ([]net.Addr, error)
}
func GetIpAddress(networkHandler NetworkHandler) (net.IP, error) {
// 获取接口列表
ifaces, err := networkHandler.GetInterfaces()
if err != nil {
return nil, err
}
// 遍历接口列表
for _, i := range ifaces {
// 只关注Wlan0或Eth0接口
if i.Name == InterfaceWlan || i.Name == InterfaceEthernet {
// 获取关联的IP地址(通常是IPv4和IPv6)
_, err := networkHandler.GetAddrsFromInterface(i)
// 处理错误
if err != nil {
return nil, err
}
// 对地址进行一些处理...
}
}
return nil, errors.New("network: ip not found")
}
测试代码
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
}
// 当调用net.Interface{}.Addrs()时的存根行为
func mockGetAddrs(i net.Interface) ([]net.Addr, error) {
return nil, errors.New("cannot get addrs")
}
用法示例
func main() {
GetIpAddress(NetworkHandler{
GetInterfaces: net.Interfaces,
GetAddrsFromInterface: func(p net.Interface) ([]net.Addr, error) {
return p.Addrs()
},
})
}
英文:
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()
},
})
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论