stretchr / testify

A toolkit with common assertions and mocks that plays nicely with the standard library
MIT License
23.47k stars 1.6k forks source link

Add command to autogenerate mocks from interfaces #273

Open ernesto-jimenez opened 8 years ago

ernesto-jimenez commented 8 years ago

The best way to use mock is with a program that will automatically generate the code for the mocks based on the interfaces.

mockery pioneered this approach but has some current limitations such as being unable to generate mocks from interfaces from other packages and incomplete mocks from composed interfaces.

goautocomplete addresses these issues but lacks documentation and reviewing the default behaviour to have less verbose go generate declarations.

Whichever implementation we use as a base, requirements would be:

Will follow up with an initial proposal

evanphx commented 8 years ago

@ernesto-jimenez I'd be happy to simply move the concept and code into testify with a new API and interface. I can deprecate mockery and tell people to move to the new thing. Because mockery is a dev time only tool, that's not a big deal.

gaffo commented 8 years ago

Any progress on this?

ernesto-jimenez commented 8 years ago

@gaffo, sorry, I've been crazy busy the past month

You can go get github.com/ernesto-jimenez/gogen/cmd/goautomock and it covers many of the points I outlined in this issue.

I hopefully will have some time in a couple of weeks to do some refinements on the usage and docs and get something rolling.

evanphx commented 8 years ago

@ernesto-jimenez I'm in the midst of updating mockery to use go/types right now. How would you like to proceed with pulling it into testify?

ernesto-jimenez commented 8 years ago

Hi @evanphx,

Before pulling something in to testify I want to give some proper thought to all of the arguments.

Once we bring the tool into testify we'll be locked-in to whatever behaviour and arguments it accepts to ensure backwards compatibility, which is why I hadn't pulled goautomock into testify yet either.

Will give it a go next week. I'll be travelling and airplane time is always good off-time for this kind of things :)

evanphx commented 8 years ago

@ernesto-jimenez No problem. We can just leave it as a separate library too, it doesn't really need to be inside testify really.

gaffo commented 8 years ago

FYI the stuff I'm doing to main in my pull requests will make it a lot easier to slap whatever cli front end you want on mockery. Which means you can solve cli compatibility by sunsetting a "CLI" and making a new one pretty easily. On Apr 7, 2016 09:03, Evan Phoenix notifications@github.com wrote:@ernesto-jimenez No problem. We can just leave it as a separate library too, it doesn't really need to be inside testify really.

—You are receiving this because you were mentioned.Reply to this email directly or view it on GitHub

ernesto-jimenez commented 8 years ago

@gaffo do you mean creating a core package that handles generating the mocks and is consumed by the main package?

That's how I build all the code generation tools too, including goautomock, the thing is that once we endorse the CLI in testify how it works should be kept backwards compatible.

ernesto-jimenez commented 8 years ago

Finally had some time to review this properly, here is what we currently have at goautomock:

Creating an interface in your code to mock a dependency:

type server interface {
  Serve(string) ([]byte, error)
}

func request(s server, path string) ([]byte, error) {
  return s.Serve(path)
}

//go:generate goautomock server

// Dummy test
func TestRequestReturnsServerError(t *testing.T) {
  m := &requestMock{}
  m.On("Serve", "/something").Return(nil, errors.New("failure"))
  _, err := request(m, "/something")
  assert.Error(t, err)
}

Mocking an interface from the standard library:

//go:generate goautomock io.Writer

// Dummy test using the generated mock
func TestWriter(t *testing.T) {
  m := &WriterMock{}
  expected := []byte("hello world")
  m.On("Write", expected).Return(11, nil)

  n, err := m.Write(expected)
  assert.Equal(t, 11, n)
  assert.Equal(t, nil, err)
}

Options:

% goautomock -h
Usage of goautomock:
  -mock-name string
        override the name for the mock struct
  -mock-pkg string
        override the package name for the mock
  -o string
        override the name of the generated code. Default value is by generated based on the name of the interface, e.g.: Reader -> reader_mock_test.go (use "-" to print to stdout)
  -pkg string
        override package to get the interface from. It can be specified in the interface name, e.g.: goautomock io.Reader (default ".")
sagikazarmark commented 4 years ago

I've built a tool for generating code and added support for generating testify mocks:

https://github.com/sagikazarmark/mga#testify-mock-generator

It works well with modules as well.

I'm working on better documentation for the project, but the tool already works and generates mocks in a real life project.

LandonTClipp commented 4 years ago

I know this is an old issue but I wanted to add some insight here. I became the de-facto maintainer of mockery a few months prior and have had a lot of time to think about how this should be done. I've created a project for a "mockery v3" here: https://github.com/vektra/mockery/projects/3

I am currently iterating on one of my other projects before I start work on v3. I've been gathering requirements and user input on what this should entail. The main point of it all is that we should be leveraging package.Load() as much as possible. mockery was made in the pre-modules era and thus has lots of historical stuff that can be removed.

Additionally, I am not married to keeping this in the vektra organization. Would love to port it to this repo eventually.

boyan-soubachov commented 4 years ago

Additionally, I am not married to keeping this in the vektra organization. Would love to port it to this repo eventually.

That sounds like a good idea, as long as we have the maintainers/contributors from the mockery project too :) Happy to discuss this in more detail if you'd like. We're on #testify-dev in the gophers Slack, or happy to email if you'd prefer.

sagikazarmark commented 4 years ago

@LandonTClipp not sure if it's relevant, but the tool I linked above is built based on mockery, but uses package.Load() under the hood. Feel free to get any ideas from there!

LandonTClipp commented 4 years ago

Cool thanks folks, I think I will get on the gophers slack. I think the way I will approach it initially is write a prototype as my own repo, then if we can get a MVP we can add it to testify as a beta release.