2

I have been unable to find a solution to mocking methods from golang packages.

For example, my project has code that attempts to recover when Os.Getwd() returns an error. The easiest way I can thinking of making a unit test for this, is by mocking the Os.Getwd() method to return an error, and verify that the code works accordingly.

I tried using testify, but it does not seem to be possible.

Anyone have any experience with that?

Gregorio Di Stefano
  • 1,173
  • 1
  • 15
  • 23

4 Answers4

3

My own solution was to take the method as an argument, which allow to inject a "mock" instead when testing. Additionnaly, create an exported method as public facade and an unexported one for testing.

Example:

    func Foo() int {        
            return foo(os.Getpid)
    }                       

    func foo(getpid func() int) int {
            return getpid()      
    } 
Elwinar
  • 9,103
  • 32
  • 40
  • Thanks, this seems like one way of doing it, but was hoping there was a simpler way of creating a mock method during testing – Gregorio Di Stefano Mar 07 '16 at 23:39
  • Well, due to its statically typed nature, mock in go are pretty raw compared to those in other languages (like PHP for example), so in terms of mock, I don't know of another solution. There may be a clever trick I don't know, though. – Elwinar Mar 08 '16 at 08:44
  • Another solution is @sargas answer, which became my way of doing once I started using files extensively: I created a `test` directory that contained a bunch of files in states I knews would make the methods I wanted fail. When dealing with the file system, I tend to find that much more simple… – Elwinar Mar 08 '16 at 08:46
  • This is the only one possible solution for mocking, but it is awful, same as awful as all go language design. – puchu May 13 '20 at 23:01
0

Looks like that taking a look at the os.Getwd test could give you some example of how you could test your code. Look for the functions TestChdirAndGetwd and TestProgWideChdir.

From reading those, it seems that the tests create temporary folders.

So a pragmatic approach would be to create temporary folders, like the tests mentioned above do, then break them so os.Getwd throws an error for you to catch on your test.

Just be careful doing these operations as they can mess up your system. I'd suggest testing in a lightweight container or a virtual machine.

sargas
  • 5,820
  • 7
  • 50
  • 69
  • 1
    This is actually not `testing`, this is just dancing around enviroment to find possible bugs. Mockering of enviroment won't reproduce all possible testing cases. – puchu May 13 '20 at 23:19
0

I know this is a bit late but, here is how you can do it.

Testing DAL or SystemCalls or package calls is usually difficult. My approach to solve this problem is to push your system function calls behind an interface and then mock the functions of those interface. For example.

type SystemCalls interface {
 Getwd() error
}


type SystemCallsImplementation struct{
}
func (SystemCallsImplementation) Getwd() error{
 return  Os.Getwd() 
}

func MyFunc(sysCall SystemCalls) error{

sysCall.Getwd()
}

With this you inject your interface that has the system calls to your function. Now you can easily create a mock implementation of your interface for testing.

like

type MockSystemCallsImplementation struct{
err error
}
func (MockSystemCallsImplementation) Getwd() error{
return err  //this can be set to nil or some value in your test function
}

Hope this answers your question.

Pharaoh
  • 712
  • 1
  • 9
  • 33
  • It is not possible to test `package` (like `os`) using this method, because it is not possible to pass package as argument in go. So you have to include your interface implementation (wrapper around `os`) into release version of application. This is awful, this should not be used by anyone. – puchu May 13 '20 at 23:23
  • I am not saying we can test system calls using this method, We can test other functionalities by putting the system calls behind an interface. Which is exactly what you are saying – Pharaoh May 15 '20 at 09:56
0

This is the limitation of go compiler, google developers don't want to allow any hooks or monkey patching. If unit tests are important for you - than you have to select a method of source code poisoning. All these methods are the following:

  • You can't use global packages directly.
  • You have to create isolated version of method and test it.
  • Production version of method includes isolated version of method and global package.

But the best solution is to ignore go language completely (if possible).

puchu
  • 3,294
  • 6
  • 38
  • 62