early commit for moq command

This commit is contained in:
Mat Ryer 2016-08-29 13:00:18 +01:00
parent 757a93b481
commit 6a2af83edc
5 changed files with 235 additions and 0 deletions

View File

@ -1,2 +1,8 @@
# moq # moq
Interface mocking tool for go generate Interface mocking tool for go generate
## Usage
```
moq Interface1
```

15
example/example.go Normal file
View File

@ -0,0 +1,15 @@
package example
import "context"
type Person struct {
ID string
Name string
Company string
Website string
}
type PersonStore interface {
Get(context.Context, string) (*Person, error)
Create(ctx context.Context, person *Person, confirm bool) error
}

11
main.go Normal file
View File

@ -0,0 +1,11 @@
package main
import (
"github.com/matryer/moq/package/moq"
)
func main() {
m := moq.New()
}

179
package/moq/moq.go Normal file
View File

@ -0,0 +1,179 @@
package moq
import (
"fmt"
"go/ast"
"go/importer"
"go/parser"
"go/token"
"go/types"
"html/template"
"io"
"os"
"strings"
)
type Mocker struct {
src string
tmpl *template.Template
fset *token.FileSet
pkgs map[string]*ast.Package
}
func New(src string) (*Mocker, error) {
fset := token.NewFileSet()
noTestFiles := func(i os.FileInfo) bool {
return !strings.HasSuffix(i.Name(), "_test.go")
}
pkgs, err := parser.ParseDir(fset, src, noTestFiles, parser.SpuriousErrors)
if err != nil {
return nil, err
}
tmpl, err := template.New("moq").Parse(moqTemplate)
if err != nil {
return nil, err
}
return &Mocker{
src: src,
tmpl: tmpl,
fset: fset,
pkgs: pkgs,
}, nil
}
// Mock generates a mock for the specified interface name.
func (m *Mocker) Mock(w io.Writer, name ...string) error {
var objs []*obj
for _, pkg := range m.pkgs {
i := 0
files := make([]*ast.File, len(pkg.Files))
for _, file := range pkg.Files {
files[i] = file
i++
}
conf := types.Config{Importer: importer.Default()}
tpkg, err := conf.Check(m.src, m.fset, files, nil)
if err != nil {
return err
}
for _, n := range name {
iface := tpkg.Scope().Lookup(n)
if !types.IsInterface(iface.Type()) {
return fmt.Errorf("%s (%s) not an interface", n, iface.Type().String())
}
iiface := iface.Type().Underlying().(*types.Interface).Complete()
obj := &obj{
Name: n,
}
for i := 0; i < iiface.NumMethods(); i++ {
meth := iiface.Method(i)
sig := meth.Type().(*types.Signature)
method := &method{
Name: meth.Name(),
}
obj.Methods = append(obj.Methods, method)
method.Params = extractArgs(sig.Params(), "in%d")
method.Returns = extractArgs(sig.Results(), "out%d")
}
objs = append(objs, obj)
}
}
err := m.tmpl.Execute(w, struct{ Objs []*obj }{Objs: objs})
if err != nil {
return err
}
return nil
}
func extractArgs(list *types.Tuple, nameFormat string) []*param {
var params []*param
for ii := 0; ii < list.Len(); ii++ {
p := list.At(ii)
name := p.Name()
if name == "" {
name = fmt.Sprintf(nameFormat, ii+1)
}
param := &param{
Name: name,
Type: p.Type().String(),
}
params = append(params, param)
}
return params
}
type obj struct {
Name string
Methods []*method
}
type method struct {
Name string
Params []*param
Returns []*param
}
func (m *method) Arglist() string {
params := make([]string, len(m.Params))
for i, p := range m.Params {
params[i] = p.String()
}
return strings.Join(params, ", ")
}
func (m *method) ArgCallList() string {
params := make([]string, len(m.Params))
for i, p := range m.Params {
params[i] = p.Name
}
return strings.Join(params, ", ")
}
func (m *method) ReturnArglist() string {
params := make([]string, len(m.Returns))
for i, p := range m.Returns {
params[i] = p.TypeString()
}
if len(m.Returns) > 1 {
return fmt.Sprintf("(%s)", strings.Join(params, ", "))
}
return strings.Join(params, ", ")
}
type param struct {
Name string
Type string
}
func (p param) String() string {
return fmt.Sprintf("%s %s", p.Name, p.TypeString())
}
func (p param) TypeString() string {
return p.Type
}
var moqTemplate = `
package todo
// AUTOGENERATED BY MOQ
// github.com/matryer/moq
{{ range .Objs }}
// {{.Name}}Mock is a mock implementation of {{.Name}}.
type {{.Name}}Mock struct {
{{ range .Methods }}
// {{.Name}}Func mocks the {{.Name}} function.
{{.Name}}Func func({{ .Arglist }}) {{.ReturnArglist}}
{{ end }}}
{{ range .Methods }}
// {{.Name}} calls the mocked {{.Name}}Func function.
func (mock *{{.Name}}Mock) {{.Name}}({{ .Arglist }}) {{.ReturnArglist}} {
if mock.{{.Name}} == nil {
panic("moq: {{.Name}}Func is nil")
}
return mock.{{.Name}}Func({{ .ArgCallList }})
}
{{ end }}
{{ end }}
`

24
package/moq/moq_test.go Normal file
View File

@ -0,0 +1,24 @@
package moq_test
import (
"bytes"
"log"
"testing"
"github.com/matryer/moq/package/moq"
)
func TestMoq(t *testing.T) {
m, err := moq.New("../../example")
if err != nil {
t.Errorf("moq.New: %s", err)
}
var buf bytes.Buffer
err = m.Mock(&buf, "PersonStore")
if err != nil {
t.Errorf("m.Mock: %s", err)
}
log.Println(buf.String())
}