bunny
bunny

Reputation: 2037

How to mock standard package function for unit test

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?

Upvotes: 1

Views: 1965

Answers (2)

Danilo
Danilo

Reputation: 193

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?

Upvotes: 1

Arash
Arash

Reputation: 1316

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.

Upvotes: 4

Related Questions