Coder Social home page Coder Social logo

minimock's Introduction

logo GoDoc Go Report Card Release Awesome

Summary

Minimock generates mocks out of Go interface declarations.

The main features of minimock are:

  • It generates statically typed mocks and helpers. There's no need for type assertions when you use minimock.
  • It's fully integrated with the standard Go "testing" package.
  • It's ready for Go modules.
  • It supports generics.
  • It works well with table driven tests because you can set up mocks for several methods in one line of code using the builder pattern.
  • It can generate several mocks in one run.
  • It can generate mocks from interface aliases.
  • It generates code that passes default set of golangci-lint checks.
  • It puts //go:generate instruction into the generated code, so all you need to do when the source interface is updated is to run the go generate ./... command from within the project's directory.
  • It makes sure that all mocked methods have been called during the test and keeps your test code clean and up to date.
  • It provides When and Then helpers to set up several expectations and results for any method.
  • It generates concurrent-safe mocks and mock invocation counters that you can use to manage mock behavior depending on the number of calls.
  • It can be used with the GoUnit tool which generates table-driven tests that make use of minimock.

Installation

If you use go modules please download the latest binary or install minimock from source:

go install github.com/gojuno/minimock/v3/cmd/minimock@latest

If you don't use go modules please find the latest v2.x binary here or install minimock using v2 branch

Usage

 minimock [-i source.interface] [-o output/dir/or/file.go] [-g]
  -g	don't put go:generate instruction into the generated code
  -h	show this help message
  -i string
    	comma-separated names of the interfaces to mock, i.e fmt.Stringer,io.Reader
    	use io.* notation to generate mocks for all interfaces in the "io" package (default "*")
  -o string
    	comma-separated destination file names or packages to put the generated mocks in,
    	by default the generated mock is placed in the source package directory
  -p string 
        comma-separated package names,
        by default the generated package names are taken from the destination directory names
  -s string
    	mock file suffix (default "_mock_test.go")

Let's say we have the following interface declaration in github.com/gojuno/minimock/tests package:

type Formatter interface {
	Format(string, ...interface{}) string
}

This will generate mocks for all interfaces defined in the "tests" package:

$ cd ~/go/src/github.com/gojuno/minimock/tests
$ minimock 

Here is how to generate a mock for the "Formatter" interface only:

$ cd ~/go/src/github.com/gojuno/minimock/tests
$ minimock -i Formatter 

Same using the relative package notation:

$ minimock -i ./tests.Formatter

Same using the full import path of the source package:

$ minimock -i github.com/gojuno/minimock/tests.Formatter -o ./tests/

All the examples above generate ./tests/formatter_mock_test.go file

Now it's time to use the generated mock. There are several ways it can be done.

Setting up a mock using the builder pattern and Expect/Return methods:

mc := minimock.NewController(t)
formatterMock := NewFormatterMock(mc).FormatMock.Expect("hello %s!", "world").Return("hello world!")

The builder pattern is convenient when you have more than one method to mock. Let's say we have an io.ReadCloser interface which has two methods: Read and Close

type ReadCloser interface {
	Read(p []byte) (n int, err error)
	Close() error
}

We can set up a mock using a simple one-liner:

mc := minimock.NewController(t)
readCloserMock := NewReadCloserMock(mc).ReadMock.Expect([]byte(1,2,3)).Return(3, nil).CloseMock.Return(nil)

But what if we don't want to check all arguments of the read method? Let's say we just want to check that the second element of the given slice "p" is 2. This is where "Inspect" helper comes into play:

mc := minimock.NewController(t)
readCloserMock := NewReadCloserMock(mc).ReadMock.Inspect(func(p []byte){
  assert.Equal(mc, 2, p[1])
}).Return(3, nil).CloseMock.Return(nil)

Setting up a mock using ExpectParams helpers:

Let's say we have a mocking interface with function that has many arguments:

type If interface {
	Do(intArg int, stringArg string, floatArg float)
}

Imagine that you don't want to check all the arguments, just one or two of them. Then we can use ExpectParams helpers, which are generated for each argument:

mc := minimock.NewController(t)
ifMock := NewIfMock(mc).DoMock.ExpectIntArgParam1(10).ExpectFloatArgParam3(10.2).Return()

Setting up a mock using When/Then helpers:

mc := minimock.NewController(t)
formatterMock := NewFormatterMock(mc)
formatterMock.FormatMock.When("Hello %s!", "world").Then("Hello world!")
formatterMock.FormatMock.When("Hi %s!", "there").Then("Hi there!")

alternatively you can use the one-liner:

formatterMock = NewFormatterMock(mc).When("Hello %s!", "world").Then("Hello world!").When("Hi %s!", "there").Then("Hi there!")

Setting up a mock using the Set method:

mc := minimock.NewController(t)
formatterMock := NewFormatterMock(mc).FormatMock.Set(func(string, ...interface{}) string {
  return "minimock"
})

You can also use invocation counters in your mocks and tests:

mc := minimock.NewController(t)
formatterMock := NewFormatterMock(mc)
formatterMock.FormatMock.Set(func(string, ...interface{}) string {
  return fmt.Sprintf("minimock: %d", formatterMock.BeforeFormatCounter())
})

Setting up expected times mock was called:

Imagine you expect mock to be called exactly 10 times. Then you can set Times helper to check how many times mock was invoked.

mc := minimock.NewController(t)
formatterMock := NewFormatterMock(mc).FormatMock.Times(10).Expect("hello %s!", "world").Return("hello world!")

There are also cases, when you don't know for sure if the mocking method would be called or not. But you still want to mock it, if it will be called. This is where "Optional" option comes into play:

mc := minimock.NewController(t)
formatterMock := NewFormatterMock(mc).FormatMock.Optional().Expect("hello %s!", "world").Return("hello world!")

When this option is set, it disables checking the call of mocking method.

Mocking context

Sometimes context gets modified by the time the mocked method is being called. However, in most cases you don't really care about the exact value of the context argument. In such cases you can use special minimock.AnyContext variable, here are a couple of examples:

mc := minimock.NewController(t)
senderMock := NewSenderMock(mc).
  SendMock.
    When(minimock.AnyContext, "message1").Then(nil).
    When(minimock.AnyContext, "message2").Then(errors.New("invalid message"))

or using Expect:

mc := minimock.NewController(t)
senderMock := NewSenderMock(mc).
  SendMock.Expect(minimock.AnyContext, "message").Return(nil)

Make sure that your mocks are being used

Often we write tons of mocks to test our code but sometimes the tested code stops using mocked dependencies. You can easily identify this problem by using minimock.NewController instead of just *testing.T. Alternatively you can use mc.Wait helper if your're testing concurrent code. These helpers ensure that all your mocks and expectations have been used at least once during the test run.

func TestSomething(t *testing.T) {
  // it will mark this example test as failed because there are no calls
  // to formatterMock.Format() and readCloserMock.Read() below
  mc := minimock.NewController(t)

  formatterMock := NewFormatterMock(mc)
  formatterMock.FormatMock.Return("minimock")

  readCloserMock := NewReadCloserMock(mc)
  readCloserMock.ReadMock.Return(5, nil)
}

Testing concurrent code

Testing concurrent code is tough. Fortunately minimock.Controller provides you with the helper method that makes testing concurrent code easy. Here is how it works:

func TestSomething(t *testing.T) {
  mc := minimock.NewController(t)

  //Wait ensures that all mocked methods have been called within the given time span
  //if any of the mocked methods have not been called Wait marks the test as failed
  defer mc.Wait(time.Second)

  formatterMock := NewFormatterMock(mc)
  formatterMock.FormatMock.Return("minimock")

  //tested code can run the mocked method in a goroutine
  go formatterMock.Format("hello world!")
}

Using GoUnit with minimock

Writing test is not only mocking the dependencies. Often the test itself contains a lot of boilerplate code. You can generate test stubs using GoUnit tool which has a nice template that uses minimock.

Happy mocking!

minimock's People

Contributors

ailurarctos avatar beono avatar damianopetrungaro avatar defaulterrr avatar denkoren avatar dependabot[bot] avatar funvit avatar genesor avatar hexdigest avatar kangaroux avatar lordvidex avatar msoap avatar rekby avatar superstas avatar suzuki-shunsuke avatar zcolleen avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

minimock's Issues

Data race in generated code

Hello guys, I probably have race in generated _mock file.
This is my test https://pastebin.com/Bt5XAxhb
go test ./ledger/light/proc/set_result_test.go -race -count 10000
full output here https://pastebin.com/EVUPkiVs
short output:

==================
WARNING: DATA RACE
Write at 0x00c0004581a0 by goroutine 7:
  sync/atomic.AddInt64()
      /usr/local/Cellar/go/1.13/libexec/src/runtime/race_amd64.s:276 +0xb
  github.com/insolar/insolar/insolar/bus.(*SenderMock).Reply()
      /Users/radist/go/src/github.com/insolar/insolar/insolar/bus/sender_mock.go:179 +0xbed
  github.com/insolar/insolar/ledger/light/proc.(*SetResult).Proceed()
      /Users/radist/go/src/github.com/insolar/insolar/ledger/light/proc/set_result.go:152 +0xef9
  command-line-arguments_test.TestSetResult_Proceed_ResultDuplicated()
      /Users/radist/go/src/github.com/insolar/insolar/ledger/light/proc/set_result_test.go:263 +0xe68
  testing.tRunner()
      /usr/local/Cellar/go/1.13/libexec/src/testing/testing.go:909 +0x199
 
Previous write at 0x00c0004581a0 by goroutine 18:
  command-line-arguments_test.TestSetResult_Proceed_HasOpenedOutgoing_Error.func3()
      /Users/radist/go/src/github.com/insolar/insolar/ledger/light/proc/set_result_test.go:696 +0x263
  github.com/insolar/insolar/ledger/light/executor.(*FilamentCalculatorMock).OpenedRequests()
      /Users/radist/go/src/github.com/insolar/insolar/ledger/light/executor/filament_calculator_mock.go:224 +0xe9b
  github.com/insolar/insolar/ledger/light/proc.(*SetResult).Proceed()
      /Users/radist/go/src/github.com/insolar/insolar/ledger/light/proc/set_result.go:157 +0x11be
  command-line-arguments_test.TestSetResult_Proceed_HasOpenedOutgoing_Error()
      /Users/radist/go/src/github.com/insolar/insolar/ledger/light/proc/set_result_test.go:715 +0x18ff
  testing.tRunner()
      /usr/local/Cellar/go/1.13/libexec/src/testing/testing.go:909 +0x199

What I did?
I've noticed that if I'm using Inspect instead of Set there is no race.
So I added lock/unlock in mocked method before "Setted" func is calling and it helps

// Reply implements Sender
func (mmReply *SenderMock) Reply(ctx context.Context, origin payload.Meta, reply *message.Message) {
	mm_atomic.AddUint64(&mmReply.beforeReplyCounter, 1)
	defer mm_atomic.AddUint64(&mmReply.afterReplyCounter, 1)

	if mmReply.inspectFuncReply != nil {
		mmReply.inspectFuncReply(ctx, origin, reply)
	}

	params := &SenderMockReplyParams{ctx, origin, reply}

	// Record call args
	mmReply.ReplyMock.mutex.Lock()
	mmReply.ReplyMock.callArgs = append(mmReply.ReplyMock.callArgs, params)
	mmReply.ReplyMock.mutex.Unlock()

	for _, e := range mmReply.ReplyMock.expectations {
		if minimock.Equal(e.params, params) {
			mm_atomic.AddUint64(&e.Counter, 1)
			return
		}
	}

	if mmReply.ReplyMock.defaultExpectation != nil {
		mm_atomic.AddUint64(&mmReply.ReplyMock.defaultExpectation.Counter, 1)
		want := mmReply.ReplyMock.defaultExpectation.params
		got := SenderMockReplyParams{ctx, origin, reply}
		if want != nil && !minimock.Equal(*want, got) {
			mmReply.t.Errorf("SenderMock.Reply got unexpected parameters, want: %#v, got: %#v%s\n", *want, got, minimock.Diff(*want, got))
		}

		return

	}
	// here
	mmReply.ReplyMock.mutex.Lock()
	defer mmReply.ReplyMock.mutex.Unlock()
	if mmReply.funcReply != nil {
		mmReply.funcReply(ctx, origin, reply)
		return
	}
	mmReply.t.Fatalf("Unexpected call to SenderMock.Reply. %v %v %v", ctx, origin, reply)

}

I'm not sure that it is the right solution. If so, I can make a pr

Added: I've replaced code inside function to sleep to exclude races inside it

sender.ReplyMock.Set(func(_ context.Context, receivedMeta payload.Meta, resMsg *message.Message) {
        time.Sleep(2 * time.Millisecond)
    })

minimock version 2.1.9
Thanks!

Catch panics in the cleanup

Hi,

Currently if a panic happens before all expected calls to mocks are made the panic output is not shown in the test output, there is only the missing expected calls.

Do you think it's possible to output both the missing expected calls and the panic ?

Checksums of v3.3.10 are wrong

https://github.com/gojuno/minimock/releases/tag/v3.3.10

Download all assets.

$ gh -R gojuno/minimock release download v3.3.10

Verify checksums, then the verification fails.

$ sha256sum -c checksums.txt 
minimock_3.3.10_darwin_amd64.tar.gz: FAILED
minimock_3.3.10_darwin_arm64.tar.gz: FAILED
minimock_3.3.10_linux_386.tar.gz: FAILED
minimock_3.3.10_linux_amd64.tar.gz: FAILED
minimock_3.3.10_linux_arm64.tar.gz: FAILED
minimock_3.3.10_windows_386.tar.gz: FAILED
minimock_3.3.10_windows_amd64.tar.gz: FAILED
minimock_3.3.10_windows_arm64.tar.gz: FAILED
sha256sum: WARNING: 8 computed checksums did NOT match

checksums.txt

5cbca9b0149e5ff023971b28df8582f183638456595db0af0bdeebe95a763234  minimock_3.3.10_darwin_amd64.tar.gz
1aec814014035b3b8577a11601e122694eb4c816d8da29bcbb5d1733591374bf  minimock_3.3.10_darwin_arm64.tar.gz
09ef2e28528e896c28d4734d5a82da10fbd0dcd8f8ac0c159c8faa5e4f80625e  minimock_3.3.10_linux_386.tar.gz
9b952ba4cff7c37dfcfbed17a2eecacb99b0ce4acd12533d17aec6d77134c687  minimock_3.3.10_linux_amd64.tar.gz
cc86c25700916bc9259dbcad080e6260ba45c0f11ad1b96d322606ab42702084  minimock_3.3.10_linux_arm64.tar.gz
cef7c3cb6b89f0354d894de2bdaf3b8294dce56ba29874e84925f30fe71aea97  minimock_3.3.10_windows_386.tar.gz
4c5d091650e476dcc4680233a97056287b9ca385cc7c6588f6c2f82cec6b3a17  minimock_3.3.10_windows_amd64.tar.gz
e73d7a90e95aa3d20252d268198f6a892e21d5c6ae5e6eb2a69d4635ddab088a  minimock_3.3.10_windows_arm64.tar.gz

Actual checksums.

$ sha256sum *.tar.gz
e86a7a19448c84366cc9671ba1eb25d2798ddf4e07522713d47b473d3f3fdc6f  minimock_3.3.10_darwin_amd64.tar.gz
08f71a6166f2c3b3b7410f1b7e1bdfe474d5a1c770a076938dddcbf8fb7beb81  minimock_3.3.10_darwin_arm64.tar.gz
2385092ccd2de382c4ac5234d9c7c7c358c14e4598e8f2160cc8cddbc2566d58  minimock_3.3.10_linux_386.tar.gz
1cddec5587ad88a3a16f4ef940fcb64f292d6e7df229ffb26a6b19ce2eb1ba94  minimock_3.3.10_linux_amd64.tar.gz
0aad764626886d2f1ba4a0825538faadfbf41edfba99e30242aeeefc4222dad6  minimock_3.3.10_linux_arm64.tar.gz
e4e362f5455ee03a965c9d3293c7cad5ea2eb4c3dfd1810d61eaa1d3d7d6f3be  minimock_3.3.10_windows_386.tar.gz
189bc10dc3b66e3cd3a74ff1ff427523123b5cadab8cc08899e26723afb59a1d  minimock_3.3.10_windows_amd64.tar.gz
ad65e2965f6d3142e8ba8e8eac4ded0e447bce72d35e3ba09a46a440177c61f5  minimock_3.3.10_windows_arm64.tar.gz

Set type name for generated mock

For example I want generate mock for interface test/test.Interface. Now generated class will have name InterfaceMock.
I want some similar to minimock -i test/test.Interface -name TestInterfaceMock and have generated name TestInterfaceMock.

And it nesessary if I want to mock two interfaces test/test.Interface and test/test2.Interface to one dest package.

Now I use workaround and create stub interface in dest package:

import "test/test"

type testInterface interface {
  test.Interface
}

oops

~/go/src/github.com/insolar/insolar$ go env
GOARCH="amd64"
GOBIN=""
GOCACHE="/Users/vany/Library/Caches/go-build"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="darwin"
GOOS="darwin"
GOPATH="/Users/vany/go"
GOPROXY=""
GORACE=""
GOROOT="/usr/local/Cellar/go/1.11/libexec"
GOTMPDIR=""
GOTOOLDIR="/usr/local/Cellar/go/1.11/libexec/pkg/tool/darwin_amd64"
GCCGO="gccgo"
CC="clang"
CXX="clang++"
CGO_ENABLED="1"
GOMOD=""
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=/var/folders/_n/nq8nymhj0j7b6ytlgryfmmvw0000gn/T/go-build087043488=/tmp/go-build -gno-record-gcc-switches -fno-common"

~/go/src/github.com/insolar/insolar$ minimock -i github.com/insolar/insolar/core.ArtifactManager -o testutils/mock/AM.go
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x12212dd]

goroutine 1 [running]:
main.main()
   /Users/vany/go/src/github.com/gojuno/minimock/cmd/minimock/minimock.go:102 +0xf3d

Ignore some arguments

As first thanks for your program.
Sorry for my english.

Is any way to ignore some arguments in Expect?

for example: i want mock method, that will receive *http.Request.

And I want check for this argument will not nil, and nothing more.

Or many method receive context.Context argument and, but context may renew internally (for example add value or timeout) and it will not same object, that I pass to checked method.

Now i see only way - Set function and do manual create function budy.

I want some syntax like:
transport.RoundTripMock.When(&http.Request{}).NotNil(0).Then(&http.Response)

for internally mock method will check about first argument not nil and ignore real value, that I passed.

And few functions like NotNil:

  • NotNil
  • Any
  • CheckBy - for function, which will check only that arg and bool result.

And other parameters will work as now: return result, when receive expected parameters.

Or syntax like:
transport.RoundTripMock.When(&http.Request{}).Adv(NotNil(0)).Then(&http.Response)

Add parallel test scenario in README

Hi, when one executes tests in parallel with t.Parallel(), and does something like: defer mockController.Finish() (similar with Wait(<duration>) ) controller fails to recognize that some of mocks' Expect/Return and other patterns are not used. Without parallel tests, there would be error like: 'Expected to call .GetMock ...'

To fix that, you need to use Cleanup method of testing.T and inside it call whether Finish or Wait.

Could be implemented like in this issue #62

I think it'd be useful to add it in README, as go official docs aren't much informative

Mock for generic interface failed

The error: failed to find any interfaces matching Interface[T in {package_name}

I have an ICache[T any] interface and I am trying to mock it with flag -i ICache[T any]

Proposal: add .Panic() alongside .Return(...) for interrupting a test

It would be useful for testing expectations which do not return an error or the error does not interrupt a flow.

example code:

func foo(dep Dependency) {
    dep.doSmth1(...)
    dep.doSmth2(...)
}

example test:

It("should doSmth1", func(){
    depMock.Expect(...).Panic()
    Ω(func(){foo(depMock)}).Should(Panic())
})

Of course the same could be done by adding panic() to the end of Inspect func, but the proposed way is more natural and shorter

Allow expected counter for concrete controller's method

Hi, I think there is an often case when we need to set expected times that concrete controller method will be executed during the test. Currently, <method>AfterCounter allows only to get the current value of calls to the mocked method, which is useless in table tests and lead to potential logical errors to pass the test.

There is a convenient method Times(N) in gomock. Could you add a similar one?

-p flag is ignored

Even if the flag is marked as DEPRECATED it's not working anymore.

$ minimock -i github.com/gojuno/minimock/tests.Stringer -o ./tests/ -p mock
DEPRECATED FLAG: -p
Generated file: tests/stringer_mock_test.go

Generates this file:

package tests

/*
DO NOT EDIT!
This code was generated automatically using github.com/gojuno/minimock v1.7
The original interface "Stringer" can be found in github.com/gojuno/minimock/tests
*/
import (
	"sync/atomic"
	"time"

	"github.com/gojuno/minimock"
)

//StringerMock implements github.com/gojuno/minimock/tests.Stringer
type StringerMock struct {
	t minimock.Tester

	StringFunc    func() (r string)
	StringCounter uint64
	StringMock    mStringerMockString
}
.....

Does not work without GOPATH env variable

Fails with error
minimock: failed to detect import path of the ...: can't detect package for file: "..."

Workaround is
GOPATH=`go env GOPATH` minimock -i path -o bla bla

Use `t.Cleanup()` in constructor

Now I have to remember to add defer or t.Cleanup in each test manually.

func TestSomething(t *testing.T) {
  formatterMock := NewFormatterMock(t)
  defer formatterMock.Finish()
  ...
}

or

func TestSomething(t *testing.T) {
  formatterMock := NewFormatterMock(t)
  t.Cleanup(formatterMock.Finish)
  ...
}

It would be great to do it automatically in mock constructor like gomock does. Of course only if it's go 1.14 or newer.

"atomic redeclared as imported package name" build error

Hi,
great tool:), but is not working with interfaces with .../atomic types like "go.uber.org/atomic".

For example for interface:

import "go.uber.org/atomic"

type At interface {
	Method(*atomic.Int64)
}

minimock generates mock:

package tt

// DO NOT EDIT!
// The code below was generated with http://github.com/gojuno/minimock (dev)

//go:generate minimock -i github.com/BrightLocal/tt.At -o ./at_mock_test.go

import (
	"sync/atomic"
	"time"

	"go.uber.org/atomic"

	"github.com/gojuno/minimock"
)
...

which produce build error:

/at_mock_test.go:12:2: atomic redeclared as imported package name
	previous declaration at ./at_mock_test.go:9:2

generating a file that doesn't compile

Minimock is generating a file that doesn't compile:
go run github.com/gojuno/minimock/v3/cmd/minimock -g -i k8s.io/client-go/kubernetes/typed/core/v1.ServiceInterface

The generated file is referencing a package (restclient) that isn't being imported.

minimock version: v3.0.9
k8s.io/client-go version: v0.21.3

go:generate from files generated with the -o parameter is broken

Let's imagine that we have an interface e.g. Fooer in the /internal/pkg/fooer directory.
We want to generate mocks for it. So we run minimock -i ./internal/pkg/fooer.Fooer -o internal/pkg/fooer to get a fooer_mock_test.go generated near the tests files.

In the generated file we have go:generate comment:
//go:generate minimock -i github.com/org/project/internal/pkg/fooer.Fooer -o ./internal/pkg/fooer/fooer_mock_test.go

Now we want to regenerate the code using go generate ./... in the project root, but it fails with:

minimock: stat internal/pkg/fooer: no such file or directory
fooer_mock_test.go:5: running "minimock": exit status 1

This is because the command in the go:generate comment should contains -o ./fooer_mock_test.go without the ./internal/pkg/fooer prefix so that the generated code is placed in the same directory that it is now.

Is it a bug or am I doing something wrong?

Skip argument check

Hey @hexdigest ! This issue is related to #30 .
First of all thank you for minimock.AnyContext , i found it very useful. But sometimes you want to skip other arguments. I know i can do something like mock.NewMock(controller).Mock.Inspect(func(arg1 string) { assert.Equal(arg1, 1) } ).Return(...) but i find it annoying to check every argument with Inspect rather then just to skip one.

You were discussing about checkers and you found it too complex and not clean. My proposal is much easier, maybe we could just add SkipMockNameArg1Name to api, so it would look like mock.NewMock(controller).Mock.Expect().SkipMockArg1Name().SkipMockArg2Name().Return(...) .

To make Expect(When) arguments cleaner, we could generate some stub variables like MockNameAnyArg1Name that will be default value of the type of the argument. Technically they will do nothing, using them will just make Expect code cleaner (that is my attempt to solve your concern about "you put into Expect not what you actually expect")

What do you think?

Flag to display version

Right now we cannot know what version we are running without generating a mock and analyzing the output.

Could you please make an improvement to have a -v flag to display the version of the minimock bin we have ?

Thanks

add v1.9.2 tag

As v2 introduces API-breaking changes, we have a lot of code that can't yet move off v1. Please could you add a v1.9.2 tag (the same as the current 1.9.2 tag) so that v1 minimock plays better with Go modules? Thanks!

Allow for checking mocked calls by hand using a call history

type Adder interface {
    Add(a int, b int) int
}

mock := NewAdderMock(mc)

mock.Add(1, 2)
mock.Add(3, 4)
mock.Add(5, 6)

// A 'Calls' attribute would contain an array of all the calls to Add()
assert.Equal(t, 3, len(mock.AddMock.Calls))

This example is somewhat useless since the arguments are known at runtime. But in cases where you may want to ignore certain arguments, or where the actual value of the arguments are not known, this would be extremely useful.

A practical example of this would be testing a function which accepts a struct argument. You may only be interested in running assertions on a couple fields. Or perhaps the struct has a randomly generated field like a SessionID, and you want to verify that it's not blank. This would also provide a solution for #30.

Additionally, there would need to be a way to disable expectation checking from mc.Finish() for a given mock. If you decide to use .Calls then that means you are handling the assertions yourself and not relying on the minimock methods. If you didn't disable the check, then you would receive an error saying that there was an unexpected call to the mock.

This could look something like this:

defer mc.Finish()
mock := NewAdderMock(mc)
mock.AddMock.Ignore()

// Continue...

Func's are missing

When I generate mocks using new version of minimock, all the functions are missing, and my old tests get compilation error, for ex: myMock.InsertFunc undefined. Is this intended or am I doing something wrong maybe? Also, mock generation goes much faster then before.

Nil panic when method not called

If set return parameters without .Expected for method with arguments, then not call the method - minimock will panic on Finish.

tmp.go

package asd

import "testing"

type Int interface {
	M()
	MA(i int)
}

func TestExpected(t *testing.T) {
	m := NewIntMock(t)
	m.MMock.Return()

	// Work as expected: fail test because method not called
	m.MinimockFinish()
}

func TestNilPanic(t *testing.T) {
	m := NewIntMock(t)
	m.MAMock.Return() // for method with arguments

	// invalid memory address or nil pointer dereference. Bug.
	m.MinimockFinish()
}

generated code:
https://gist.github.com/rekby/3e95b03c8220cd5da895e8ccaa66bd44

Generation for interfaces with no methods

Hi! Thanks for this library, it really helped me to understand mocks fully! Hope it will get some new awesome features.
So, I have an application that uses GitHub.com/olivere/elastic (We are using a lot from this library), that's why I tried to mock all interfaces from it, in my generate.go:
//go:generate minimock -i github.com/olivere/elastic.* -o ./
And I'm getting something like:

minimock: ./database_mock_test.go
minimock: ./smoothing_model_mock_test.go
minimock: ./candidate_generator_mock_test.go
minimock: ./rescorer_mock_test.go
minimock: ./suggester_mock_test.go
minimock: ./backoff_mock_test.go
minimock: ./score_function_mock_test.go
minimock: ./aggregation_mock_test.go
minimock: ./composite_aggregation_values_source_mock_test.go
minimock: ./query_mock_test.go
minimock: ./bulkable_request_mock_test.go
minimock: ./sorter_mock_test.go
minimock: ./doer_mock_test.go
minimock: ./alias_action_mock_test.go
minimock: ./retrier_mock_test.go
minimock: interface has no methods

That's why I've failed to generate mocks for the rest of the interfaces. I think it can be great to just add warning message and continue the generation process. WDYT about it?

missing imports

internal/pkg/app/app.go

package app

import (
	desc "<github path to>/internal/pkg/protobuf"
)

type App interface {
    Run(*desc.Args) error
}

/internal/pkg/protobuf

package protobuf

type Args struct {
	Y int
}

internal/app/mock/doc.go

package mock

Run minimock version 2.1.0
minimock -i "./internal/pkg/app.*" -o "./internal/app/mock/" -s "_mock.go"
and got

package mock

// DO NOT EDIT!
// The code below was generated with http://github.com/gojuno/minimock (2.1.0)

//go:generate minimock -i <github path to>/internal/pkg/app.App -o ./internal/app/mock/app_mock.go

import (
	"sync/atomic"
	"time"

	"github.com/gojuno/minimock"
)

// AppMock implements app.App
type AppMock struct {
	t minimock.Tester

	funcRun          func(ap1 *desc.Args) (err error)
	afterRunCounter  uint64
	beforeRunCounter uint64
	RunMock          mAppMockRun
}
....

missing import is desc "<github path to>/internal/pkg/protobuf"

-o breaks on empty directories

Specifying an empty output directory for -o option results in minimock failing.

Steps to reproduce:

$ mkdir mocks
$ go run github.com/gojuno/minimock/cmd/minimock -g -i io.Reader -o mocks/ -s _mock.go
minimock: failed to load destination package: ./mocks: -: unknown import path "your/module/pkg/mocks": cannot find module providing package your/module/pkg/mocks
exit status 1

Implement a way to skip checking expectations for a mock object

Based on #35.

It would be great if a mock could be skipped when the mock controller checks to see if expectations were met. This has several use cases:

  • Argument values are not known at runtime (e.g. random)
  • Argument value is complex (e.g. struct with private fields)
  • Mocking an interface to either make a no-op implementation or to capture call args (satisfies both the above points and reduces boilerplate)

Some possible naming schemes:

FooMock.ExpectAnything().Return("val")
FooMock.SkipExpectations()
FooMock.CaptureOnly()

ExpectAnything implies that the mock will be called at least once. SkipExpectations is the least vague IMO. CaptureOnly makes it sound like a Return() wouldn't work which probably isn't the desired functionality.

no new variables on left side of :=

Generates incorrect code if function has params param.

Example:

type I interface {
	F(params []int) int
}

Output:

// F implements a.I
func (mmF *IMock) F(params []int) (i1 int) {
	mm_atomic.AddUint64(&mmF.beforeFCounter, 1)
	defer mm_atomic.AddUint64(&mmF.afterFCounter, 1)

	if mmF.inspectFuncF != nil {
		mmF.inspectFuncF(params)
	}

	params := &IMockFParams{params}

	// Record call args
	mmF.FMock.mutex.Lock()
	mmF.FMock.callArgs = append(mmF.FMock.callArgs, params)
	mmF.FMock.mutex.Unlock()

	for _, e := range mmF.FMock.expectations {
		if minimock.Equal(e.params, params) {
			mm_atomic.AddUint64(&e.Counter, 1)
			return e.results.i1
		}
	}

	if mmF.FMock.defaultExpectation != nil {
		mm_atomic.AddUint64(&mmF.FMock.defaultExpectation.Counter, 1)
		want := mmF.FMock.defaultExpectation.params
		got := IMockFParams{params}
		if want != nil && !minimock.Equal(*want, got) {
			mmF.t.Errorf("IMock.F got unexpected parameters, want: %#v, got: %#v%s\n", *want, got, minimock.Diff(*want, got))
		}

		results := mmF.FMock.defaultExpectation.results
		if results == nil {
			mmF.t.Fatal("No results are set for the IMock.F")
		}
		return (*results).i1
	}
	if mmF.funcF != nil {
		return mmF.funcF(params)
	}
	mmF.t.Fatalf("Unexpected call to IMock.F. %v", params)
	return
}```

go get -u Fails with Minimock

Lately (since Minimock started using Go modules, it seems) we’ve had trouble running go get -u on several projects that use Minimock. It always fails with:

go: github.com/gojuno/[email protected]: go.mod has post-v0 module path "github.com/gojuno/minimock/v3" at revision 7e61077391e9

(The timestamp and commit ID aren’t always the same, but seem to point to the latest release.)

We believe the cause is Minimock, since we’re not seeing anything like it with any other module, and the error can be reproduced with a minimal project containing only a main.go:

package main

import (
	_ "github.com/gojuno/minimock/v3"
)

func main() {}

And running go mod init example.com/test creates a go.mod file like:

module example.com/test

go 1.12

require github.com/gojuno/minimock/v3 v3.0.2

Then running go get -u results in the error above.

Not that we claim to fully understand the error messsage, but could there be something wrong with Minimock’s go.mod? Or is there something we’re doing wrong?


Thanks in advance and thanks for the nice software!

FeatureRequest: named input arguments for functions

For example, I have an interface:

type FileUploader interface {
	Save(ctx context.Context, Data []byte, Filename string) (*FileInfo, error)
}

Than, I have to work with arguments p, p1, p2.

mockFileStorage.SaveMock.Set(func(p context.Context, p1 []byte, p2 string) (r *FileInfo, r1 error) {

Instead, I would like to see names of variables according to interface declaration (ctx, Data, Filename).
Also I don't think we need to pre allocate return values (r and r1)

mockFileStorage.SaveMock.Set(func(ctx context.Context, Data []byte, Filename string) (*FileInfo, error) {

Use more unique variable name for mock

I have a generated mock that doesn't compile because it has a method where one of the arguments has the name m. This conflicts with the m name given to the mock itself in the Expect method:

func (m *mContextManager_ContextParticipantStreamServerMockRecvMsg) Expect(m interface{}) *mContextManager_ContextParticipantStreamServerMockRecvMsg {
...

This could be fixed by having a more unique name for the mock (in template.go?), I think.

Release tags are versioned as "dev"

  1. Run go get github.com/gojuno/minimock/v3/cmd/[email protected]
  2. Run minimock --version
  3. Output:
MiniMock version dev
Git commit: dev
Build date: 2021-01-22T00:47:47Z
  1. What I expected to see:
MiniMock version 3.0.8
Git commit: 3.0.8
Build date: 2021-01-22T00:47:47Z

In addition the top of the mocks all say:

// Code generated by http://github.com/gojuno/minimock (dev). DO NOT EDIT.

Instead of (3.0.8)

If you download the tagged release source code it shows as "dev"[0]:


	//do not modify the following vars
	//the values are being injected at the compile time by goreleaser
	version   = "dev"
	commit    = "dev"

[0] https://github.com/gojuno/minimock/archive/v3.0.8.zip

Generated invalid mock file when return type is channel

Hi!
I have interface like this

type Consumer interface {
	GetDone() chan error
        ...
}

And when generated mock file with
minimock -i .Consumer

Inside it have
funcGetDone func() (ch1 error)
func (mmGetDone *mConsumerMockGetDone) Return(ch1 error) *ConsumerMock
func (mmGetDone *mConsumerMockGetDone) Set(f func() (ch1 error)) *ConsumerMock
func (mmGetDone *ConsumerMock) GetDone() (ch1 error)

Which breaks interface requirements

As a temporary workaround, you can break method into two like this:

GetDoneWrite() chan<- error
GetDoneRead() <-chan error

Then it can generate right signatures
funcGetDoneRead func() (ch1 <-chan error)
funcGetDoneWrite func() (ch1 chan<- error)

Interface from same package name as dependency broken since v3.2.1

Hello,

Since v3.2.1, minimock generates import groups that don't compile, for interfaces importing types from other packages with the same name as the interface's package.

Steps to reproduce

Consider this directory structure:

├── pb
│   └── interface.go
├── dependency
│   └── pb
│       └── dep.go
└── gen.go

With the following contents:

// dependency/pb/dep.go
package pb

type Item struct {
	A string
}
// pb/interface.go
package pb

import (
	"mymodule/dependency/pb"
)

type Interface interface {
	A() pb.Item
}
// gen.go
package mymodule

//go:generate minimock -i ./pb.Interface -g

Generated import group in v3.2.0

import (
	"mymodule/dependency/pb"
	mm_atomic "sync/atomic"
	mm_time "time"

	"github.com/gojuno/minimock/v3"
)

Generated import group in v3.2.1

import (
	"mymodule/pb"

	"mymodule/dependency/pb"
	mm_atomic "sync/atomic"
	mm_time "time"

	"github.com/gojuno/minimock/v3"
)

Which raises the error 'pb' redeclared as the imported package name

Follow Go Modules release versioning

The latest version (v2.1.5) causes go list and some other tools to fail:

$ go list
go: errors parsing go.mod:
/home/sergey/go/src/indeed.com/devops/snitch/go.mod:6: invalid module: github.com/gojuno/minimock should be v0 or v1, not v2 (v2.1.5)

Using one of the approaches suggested in Releasing Modules (v2 or Higher) would resolve this.

Workaround: depend on minimock using pseudo-versions as if it is not a proper go module. See this doc for details.

Add flag to specify build tags

I use minimock for my project and really like it. In that project, I have a lot of mocks. I want to move these mocks to separate package and exclude from the build for production. Hence, I need an ability to specify build tags during generating mocks.

Simplify

I use table tests and run each test inside a testing.T.Run closure and prefer to complete all test validation within the closure. To do this with minimock, I setup the mocks in my test cases with a nil minimock.Tester (NewXMock(nil)) and then within the closure, I do a type assertion on the mock, create a new controller, set the mock's tester and register the mock with the controller. Ex:

tests := []struct {
	name string
	x        X // an interface
	// additional test case details
}{
	{
		name: "testName",
		x:        NewXMock(nil),
		// additional test case setup
	},
}
for _, tt := range tests {
	tt := tt
	t.Run(tt.name, func(t *testing.T) {
		if m, ok := tt.x.(*XMock); ok {
			c := minimock.NewController(t)
			defer c.Finish()
			m.t = c
			c.RegisterMocker(m)
		}
		// perform test
	})
}

I would like to do something like:

c := minimock.NewController(t)
defer c.Finish()
c.AttachMock(tt.x)

Where AttachMock is defined like:

func (c Controller) AttachMock(m interface{}) {
	if m, ok := m.(interface{ SetTester(Tester) }); ok {
		m.SetTester(c)
	}
}

And generated mocks have:

func (m *XMock) SetTester(t minimock.Tester) {
	m.t = t

	if controller, ok := t.(minimock.MockController); ok {
		controller.RegisterMocker(m)
	}
}

Use "go run" in the generated go:generate

Running minimock without -g flag produces a go:generate line that looks like this:

//go:generate minimock -i InterfaceName ...

minimock can resolve to any command on the system go:generate is run on (most likely, older versions of minimock). Let's change the generated command to go run github.com/gojuno/minimock/v3/cmd/minimock so that the version from the module's dependencies is used.

That way it'll play nicely with the recommended tool dependency tracking approach

Test failures on 32 bit architectures

armv7hl: https://koji.fedoraproject.org/koji/taskinfo?taskID=73283110

      testing: github.com/gojuno/minimock
github.com/gojuno/minimock
PASS
ok  	github.com/gojuno/minimock	0.011s
github.com/gojuno/minimock/tests
--- FAIL: TestFormatterMock_UnmockedCallFailsTest (0.00s)
panic: unaligned 64-bit atomic operation
	panic: unaligned 64-bit atomic operation [recovered]
	panic: unaligned 64-bit atomic operation
goroutine 7 [running]:
testing.tRunner.func1.2(0x693728, 0x6bcdb0)
	/usr/lib/golang/src/testing/testing.go:1143 +0x294
testing.tRunner.func1(0x1c01880)
	/usr/lib/golang/src/testing/testing.go:1146 +0x3b4
panic(0x693728, 0x6bcdb0)
	/usr/lib/golang/src/runtime/panic.go:965 +0x188
runtime/internal/atomic.panicUnaligned()
	/usr/lib/golang/src/runtime/internal/atomic/unaligned.go:8 +0x2c
runtime/internal/atomic.Load64(0x1d12bac, 0x1c38508, 0x0)
	/usr/lib/golang/src/runtime/internal/atomic/asm_arm.s:263 +0x14
github.com/gojuno/minimock/tests.(*TesterMock).MinimockFatalfDone(0x1d12a80, 0x4cb101)
	/builddir/build/BUILD/minimock-3.0.9/_build/src/github.com/gojuno/minimock/tests/tester_mock_test.go:931 +0xc4
github.com/gojuno/minimock/tests.(*TesterMock).minimockDone(0x1d12a80, 0x0)
	/builddir/build/BUILD/minimock-3.0.9/_build/src/github.com/gojuno/minimock/tests/tester_mock_test.go:998 +0x6c
github.com/gojuno/minimock/tests.(*TesterMock).MinimockFinish(0x1d12a80)
	/builddir/build/BUILD/minimock-3.0.9/_build/src/github.com/gojuno/minimock/tests/tester_mock_test.go:961 +0x1c
panic(0x693728, 0x6bcdb0)
	/usr/lib/golang/src/runtime/panic.go:965 +0x188
runtime/internal/atomic.panicUnaligned()
	/usr/lib/golang/src/runtime/internal/atomic/unaligned.go:8 +0x2c
runtime/internal/atomic.Xadd64(0x1d12bb4, 0x1, 0x0, 0x4ed364, 0x6e4b70)
	/usr/lib/golang/src/runtime/internal/atomic/asm_arm.s:233 +0x14
github.com/gojuno/minimock/tests.(*TesterMock).Fatalf(0x1d12a80, 0x636e20, 0x2e, 0x1c0e220, 0x2, 0x2)
	/builddir/build/BUILD/minimock-3.0.9/_build/src/github.com/gojuno/minimock/tests/tester_mock_test.go:854 +0x4c
github.com/gojuno/minimock/tests.(*FormatterMock).Format(0x1d0e8a0, 0x637987, 0x32, 0x0, 0x0, 0x0, 0x0, 0x0)
	/builddir/build/BUILD/minimock-3.0.9/_build/src/github.com/gojuno/minimock/tests/formatter_mock.go:186 +0x32c
github.com/gojuno/minimock/tests.TestFormatterMock_UnmockedCallFailsTest(0x1c01880)
	/builddir/build/BUILD/minimock-3.0.9/_build/src/github.com/gojuno/minimock/tests/formatter_mock_test.go:31 +0x128
testing.tRunner(0x1c01880, 0x6bc52c)
	/usr/lib/golang/src/testing/testing.go:1193 +0xd8
created by testing.(*T).Run
	/usr/lib/golang/src/testing/testing.go:1238 +0x254
exit status 2
FAIL	github.com/gojuno/minimock/tests	0.013s

i686: https://koji.fedoraproject.org/koji/taskinfo?taskID=73283111

      testing: github.com/gojuno/minimock
github.com/gojuno/minimock
PASS
ok  	github.com/gojuno/minimock	0.007s
github.com/gojuno/minimock/tests
--- FAIL: TestFormatterMock_UnmockedCallFailsTest (0.00s)
panic: unaligned 64-bit atomic operation
	panic: unaligned 64-bit atomic operation [recovered]
	panic: unaligned 64-bit atomic operation
goroutine 22 [running]:
testing.tRunner.func1.2(0x5680ff60, 0x5683d574)
	/usr/lib/golang/src/testing/testing.go:1143 +0x2ce
testing.tRunner.func1(0x57082fc0)
	/usr/lib/golang/src/testing/testing.go:1146 +0x3fc
panic(0x5680ff60, 0x5683d574)
	/usr/lib/golang/src/runtime/panic.go:971 +0x491
runtime/internal/atomic.panicUnaligned()
	/usr/lib/golang/src/runtime/internal/atomic/unaligned.go:8 +0x38
runtime/internal/atomic.Load64(0x570d6bac, 0x570334d4, 0x0)
	/usr/lib/golang/src/runtime/internal/atomic/asm_386.s:201 +0x10
github.com/gojuno/minimock/tests.(*TesterMock).MinimockFatalfDone(0x570d6a80, 0x567b0b01)
	/builddir/build/BUILD/minimock-3.0.9/_build/src/github.com/gojuno/minimock/tests/tester_mock_test.go:931 +0xab
github.com/gojuno/minimock/tests.(*TesterMock).minimockDone(0x570d6a80, 0x567b0a15)
	/builddir/build/BUILD/minimock-3.0.9/_build/src/github.com/gojuno/minimock/tests/tester_mock_test.go:998 +0x64
github.com/gojuno/minimock/tests.(*TesterMock).MinimockFinish(0x570d6a80)
	/builddir/build/BUILD/minimock-3.0.9/_build/src/github.com/gojuno/minimock/tests/tester_mock_test.go:961 +0x22
panic(0x5680ff60, 0x5683d574)
	/usr/lib/golang/src/runtime/panic.go:971 +0x491
runtime/internal/atomic.panicUnaligned()
	/usr/lib/golang/src/runtime/internal/atomic/unaligned.go:8 +0x38
runtime/internal/atomic.Xadd64(0x570d6bb4, 0x1, 0x0, 0x570a26d0, 0x90)
	/usr/lib/golang/src/runtime/internal/atomic/asm_386.s:107 +0x11
github.com/gojuno/minimock/tests.(*TesterMock).Fatalf(0x570d6a80, 0x567bf08b, 0x2e, 0x570b2200, 0x2, 0x2)
	/builddir/build/BUILD/minimock-3.0.9/_build/src/github.com/gojuno/minimock/tests/tester_mock_test.go:854 +0x48
github.com/gojuno/minimock/tests.(*FormatterMock).Format(0x570d28a0, 0x567bfbf2, 0x32, 0x0, 0x0, 0x0, 0x0, 0x0)
	/builddir/build/BUILD/minimock-3.0.9/_build/src/github.com/gojuno/minimock/tests/formatter_mock.go:186 +0x35e
github.com/gojuno/minimock/tests.TestFormatterMock_UnmockedCallFailsTest(0x57082fc0)
	/builddir/build/BUILD/minimock-3.0.9/_build/src/github.com/gojuno/minimock/tests/formatter_mock_test.go:31 +0x14d
testing.tRunner(0x57082fc0, 0x5683ccf0)
	/usr/lib/golang/src/testing/testing.go:1193 +0x102
created by testing.(*T).Run
	/usr/lib/golang/src/testing/testing.go:1238 +0x233
exit status 2
FAIL	github.com/gojuno/minimock/tests	0.011s

Run tests on CI

It seems no test and lint aren't run on CI.
How about running tests and lint on CI with GitHub Actions?

  • go vet
  • go test

Then we can find issues quickly before merging pull requests.

If this proposal is acceptable, I'm interested in working on this.

Comparison with gomock

I am currently using the mockgen utility that comes with gomock to generate mocks for any interface (both inside the app and also for imported go modules)

https://github.com/golang/mock

A comparison of this tool and gomock+mockgen would be really awesome in my opinion

Release minimock by CI

Now prebuilt binaries are released by executing GoReleaser on the maintainer's lap top.

#79 (comment)

I think it's great if we can release minimock by CI such as GitHub Actions.
We can automate the release process.
When we face issues about releases, we can look into them if the release is executed on CI.
On the other hand, if the release is run on maintainers' lap top, other than maintainers can't contribute to issues.

If the proposal is acceptable, I'm interested in working on this.

Enable generating mocks for interfaces in any stdlib package

It appears there's a fixed set of standard library packages minimock can generate mocks for. For example, go run github.com/gojuno/minimock/cmd/minimock -g -i io.Reader succeeds while the following fails:

$ go run github.com/gojuno/minimock/cmd/minimock -g -i http.ResponseWriter
minimock: -: unknown import path "http": cannot find module providing package http
exit status 1

suffix flag has been removed

suffix = flag.String("s", "_mock_test.go", "output file name suffix which is added to file names when multiple interfaces are given")
Hi, there was a suffix flag before switch to gowrap, it wasn't marked as deprecated. I would be very grateful if you could bring that flag back.

Version flag is missing

Hi,

There was a flag -version and now it's missing for some reason.
#9

It would be nice to bring it back

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.