🚀 Big News: Socket Acquires Coana to Bring Reachability Analysis to Every Appsec Team.Learn more
Socket
DemoInstallSign in
Socket

github.com/jonnylangefeld/injector

Package Overview
Dependencies
Alerts
File Explorer
Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

github.com/jonnylangefeld/injector

v1.0.4
Source
Go
Version published
Created
Source

Go Report Card codecov Lint & Test Twitter GitHub release GitHub

Shim

The most user friendly shimming library for go!

Use this library to overwrite a function from an imported package (that isn't available via an interface) with your own stub for testing.

Look & Feel

Let's assume we have a function something() that calls os.Create() under the hood. But for unit testing we don't actually want to create a file every time or we want to test the behavior in case of an error on create. Let's just replace the original with a stub where we control what it returns.

First, remove the dot . for imported functions you want to mock and declare them as a variable:

something.go:

 import "os"

+var(
+  osCreate = os.Create
+)

 func something() {
-  file, err := os.Create("./foo")
+  file, err := osCreate("./foo")
 }

Then, replace osCreate with something you control in your unit test. For instance return a test error.

something_test.go:

func TestSomething(t *testing.T) {
  shim.Run(
    func() {
      // put anything you want run and assert in a test in here as you normally would
      err := something()
      assert.Error(t, err)
    },
    // Add a list of replacements using `Replace(&original).With(replacement)`.
    // We can simply define a stub here that returns what we want for testing.
    shim.Replace(&osCreate).
      With(func(name string) (*os.File, error) {
        return nil, fmt.Errorf("test error")
      }),
  )
}

Inject receiver functions

It also works for receiver functions on structs of 3rd party libraries that don't offer interfaces, we just have to instantiate the object and the function as a var:

something.go:

 import "os"

+var(
+  file     *os.File
+  fileRead = file.Read
+)

 func something() {
   file, err := osCreate("./foo")
-  _, err = file.Read([]byte{})
+  _, err = fileRead([]byte{})
 }

The injection works similar to described above, just add another replacement into the shim.Run() function:

  shim.Run(
    ...
    shim.Replace(&fileRead).
      With(func(b []byte) (n int, err error) {
        return 0, fmt.Errorf("test error")
      }),
  )

For integrated examples check out examples/main.go and examples/main_test.go.

Why another library?

This library essentially offers a nice API around this manual shimming pattern:

func TestSomething(t *testing.T) {
  osCreateOrig := osCreate
  osCreate = func(name string) (*os.File, error) {
    return nil, fmt.Errorf("test error")
  }

  err := something()
  assert.Error(t, err)

  osCreate = osCreateOrig
}

The issue with this is that you always have to store the original function and restore it at the end. Otherwise it can have unintentional side effects on other test executions.

The API is also fully typed using go generics. Whatever type you put in Replace() you have to put into With() as well, as the type gets inferred by the first call. Your IDE will give you those type hints as code completion.

An alternative to this approach is to create an interface where the production implementation actually calls the underlying function and a test implementation mocks the response. However, that's more verbose for types that don't already offer an interface in the 3rd party library and also with this approach the production implementation will leave over some untested lines of code.

For library functions that already offer interfaces I don't recommend this library, but rather gomock.

FAQs

Package last updated on 31 Jul 2024

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts