如何为单元测试模拟标准包函数

huangapple go评论68阅读模式
英文:

How to mock standard package function for unit test

问题

我有三个函数:

func IsSymlinks(path string) {
   ...
   ...
}

func (c *MyClass) myFunc1(path string) {
  ...更多代码
  ...更多代码
  if IsSymlinks(path) {
    realPath := filepath.EvalSymlinks(path)
  }
  ...更多代码
  ...更多代码
}

func myFunc2(path string) {
      ...更多代码
      ...更多代码
      if IsSymlinks(path) {
        realPath := filepath.EvalSymlinks(path)
      }
      ...更多代码
      ...更多代码
}

我如何通过模拟filepath.EvalSymlinksIsSymlinks来测试myFunc1myFunc2函数?我搜索了一些帖子并看到了几种解决方案。

  1. 创建一个函数类型的EvalSymlinks变量,用filepath.EvalSymlinks进行初始化,然后在测试包中将其更改为我的实现。然而,我更倾向于不使用这种方法。

  2. filepath.EvalSymlinksIsSymlinks作为参数传递给myFunc。我也不太喜欢使用这种方式。

  3. 很多人谈论使用接口进行模拟。你能帮忙吗?或者还有其他可以测试myFunc的方法吗?谢谢!

  4. 我还在考虑的另一个选择是,在进行函数测试之前,是否将一个符号链接创建到一个路径中,然后在测试完成后立即删除该符号链接?

英文:

I have three functions:

func IsSymlinks(path string) {
   ...
   ...
}

func (c *MyClass) myFunc1(path string) {
  ...more code
  ...more code
  if IsSymlinks(path) {
    realPath := filepath.EvalSymlinks(path)
  }
  ...more code
  ...more code
}

func myFunc2(path string) {
      ...more code
      ...more code
      if IsSymlinks(path) {
        realPath := filepath.EvalSymlinks(path)
      }
      ...more code
      ...more code
    }

How do I test myFunc1 and myFunc2 by mocking filepath.EvalSymlinks and IsSymlinks? I searched some posts and saw several solutions.

  1. Create a EvalSymlinks variable of function type, initialized with filepath.EvalSymlinks, and then in the test package, change it to my implementation. However, I prefer not to use this approach.

  2. Pass in filepath.EvalSymlinks and IsSymlinks into myFunc as parameter. I prefer not to use this way either.

  3. A lot of people talk about mocking use interface. Could you please help? Or another approach that could test myFunc? Thank you!

  4. Another option I am thinking is if it is a good practice to create a symlink in os to a path before I do the function test, and then delete the symlink right after testing?

答案1

得分: 3

你可以通过使用接口来实现。例如,假设你有一个名为Linker的接口:

type Linker interface {
    IsSymlinks(path string)
}

现在你可以将一个Linker对象嵌入到调用IsSymlinks方法的函数中:

type MyClass struct {
    Linker
}

func (p *MyClass) myFunc(path string) {
    _ = p.Linker.IsSymlinks(path)
}

现在在你的测试中,你可以创建一个模拟的Linker:

type mockLinker struct{}
// 根据需要实现mockLinker上的IsSymlinks方法

p := &MyClass{
    Linker: mockLinker{},
}

p.myFunc(path)

p.myFunc调用IsSymlinks方法时,它会调用你模拟的IsSymlinks方法。

对于filepath.EvalSymlinks(path),你可以将该方法包装在另一个方法中,并将新方法添加到相同的接口中,然后模拟该方法而不是模拟EvalSymlinks

仅为了测试目的而创建接口可能并不总是最好的主意,因为在某些情况下,这可能导致拥有大型接口,这在Go中不符合惯例,因为它会使代码变得不太可读。

英文:

You can do it by using an interface. For example let's say you have an interface called Linker:

type Linker interface {
    IsSymlinks(path String)
}

Now you can embed a Linker object into the function that calls the IsSymlinks method.

type MyClass struct {
    Linker
}

func (p *MyClass) myFunc(path String) {
    _ = p.Linker.IsSymlinks(path)
}

Now in your test you can create a mock Linker.

type mockLinker struct{}
//implement the IsSymlinks on the mockLinker as you wish

p := &MyClass{
    Linker: mockLinker,
}

p.myFunc(path)

when p.myFunc reaches the IsSymlinks method it calls your mocked IsSymlinks method.

For filepath.EvalSymlinks(path) you can wrap that method in another method that is yours and add the new method to the same interface and mock that method instead of mocking the EvalSymlinks.

Creating interfaces just for the purpose of testing may not always be the best idea as in some cases may result in having large interfaces which are not idiomatic in Go as it makes the code less readable.

答案2

得分: 1

我认为你可以尝试以不同的方式设计你的MyClass结构和相关函数。我的意思是让你的MyClass对象与filepath等包保持独立。首先,创建一个新的接口,其中包含两个函数:

type MyEvalLink interface {
    IsSymlinks(path string) bool
    EvalSymlinks(path string) (string, error)
}

然后,你可以在一个使用filepath包或虚拟代码的结构体中实现这两个函数:

type EvalLinkUse struct{}

func (p *EvalLinkUse) IsSymlinks(path string) bool {
    // 在这里放入你的真实代码或虚拟代码
}

func (p *EvalLinkUse) EvalSymlinks(path string) (string, error) {
    // 在这里放入你的真实代码或虚拟代码
}

这样,你的代码将会改变如下:

type MyClass struct {
    ...你的代码
    MyEvalLink MyEvalLink
}

func (c *MyClass) myFunc1(path string) {
    ...更多的代码
    ...更多的代码
    if c.MyEvalLink.IsSymlinks(path) {
        realPath, err := c.MyEvalLink.EvalSymlinks(path)
        // 在这里处理realPath和err
    }
    ...更多的代码
    ...更多的代码
}

这样做是否适合你的情况?

英文:

I think you can try to design in a different way your MyClass struct and the related functions. I mean to get indipendent your MyClass object from packages like filepath. First, create a new interface that have two functions:

type MyEvalLink interface {
	IsSymlinks(path string) bool
	EvalSymlinks(path string) (string, error)
}

Then you can implement both the two functions in a struct that use the filepath package or a fake code:

type EvalLinkUse struct{}

func (p *EvalLinkUse) IsSymlinks(path string) bool {
  // put here your real code or fake
}

func (p *EvalLinkUse) EvalSymlinks(path string) (string, error) {
  // put here your real code or fake
}

So, your code will change as follow:

type MyClass struct {
    ...your code
    MyEvalLink MyEvalLink
} 



func (c *MyClass) myFunc1(path string) {
  ...more code
  ...more code
  if c.MyEvalLink.IsSymlinks(path) {
    realPath := c.MyEvalLink.EvalSymlinks(path)
  }
  ...more code
  ...more code
}

Could that fit your case?

huangapple
  • 本文由 发表于 2018年7月8日 10:46:41
  • 转载请务必保留本文链接:https://go.coder-hub.com/51228459.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定