Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/recruit-tech/dicon

DICONtainer Generator for go.
https://github.com/recruit-tech/dicon

Last synced: about 11 hours ago
JSON representation

DICONtainer Generator for go.

Awesome Lists containing this project

README

        

# dicon

DICONtainer Generator for go.

[![CircleCI](https://circleci.com/gh/recruit-tech/dicon.svg?style=svg)](https://circleci.com/gh/recruit-tech/dicon)
[![Go Report Card](https://goreportcard.com/badge/github.com/recruit-tech/dicon)](https://goreportcard.com/report/github.com/recruit-tech/dicon)

## Getting Started

### Prerequisites
- Go 1.9+
- make

### Installing
```
$ go get -u github.com/recruit-tech/dicon/cmd/dicon
```

### How to use
1. Write container interface and comment `+DICON` over it.

```container.go
// +DICON
type Container interface {
UserService() (UserService, error)
UserRepository() (UserRepository, error)
}
```

2. Prepare dependencies. You must write constructor which meets below requirements:
- method name must be `New` + Interface name
- return type must be (Interface, error) tuple.
- dependencies which use this instance must be passed via the constructor.

```user.go
type User struct {
ID int64
Name string
}
```

```userrepository.go
type UserRepository interface {
FindByID(id int64) (*User, error)
}

type userRepository struct{}

func (*userRepository) FindByID(id int64) (*User, error) {
// STUB
return &User{ID: id, Name: "foo"}, nil
}

func NewUserRepository() (UserRepository, error) {
return &userRepository{}, nil
}
```

```userservice.go
type UserService interface {
Find(id int64) (*User, error)
}

type userService struct {
repo UserRepository
}

func (us *userService) Find(id int64) (*User, error) {
return us.repo.FindByID(id)
}

func NewUserService(repo UserRepository) (UserService, error) {
return &userService{
repo: repo,
}, nil
}
```

3. generate!
```
$ dicon generate --pkg sample
```

4. You can get the container implementation!

```dicon_gen.go
// Code generated by "dicon"; DO NOT EDIT.

package sample

import (
"fmt"

"github.com/pkg/errors"
)

type dicontainer struct {
store map[string]interface{}
}

func NewDIContainer() Container {
return &dicontainer{
store: map[string]interface{}{},
}
}

func (d *dicontainer) UserRepository() (UserRepository, error) {
if i, ok := d.store["UserRepository"]; ok {
instance, ok := i.(UserRepository)
if !ok {
return nil, fmt.Errorf("invalid instance is cached %v", instance)
}
return instance, nil
}
instance, err := NewUserRepository()
if err != nil {
return nil, errors.Wrap(err, "creation UserRepository failed at DICON")
}
d.store["UserRepository"] = instance
return instance, nil
}
func (d *dicontainer) UserService() (UserService, error) {
if i, ok := d.store["UserService"]; ok {
instance, ok := i.(UserService)
if !ok {
return nil, fmt.Errorf("invalid instance is cached %v", instance)
}
return instance, nil
}
dep0, err := d.UserRepository()
if err != nil {
return nil, errors.Wrap(err, "resolve UserRepository failed at DICON")
}
instance, err := NewUserService(dep0)
if err != nil {
return nil, errors.Wrap(err, "creation UserService failed at DICON")
}
d.store["UserService"] = instance
return instance, nil
}
```

5. Use it!
```.go
di := NewDIContainer()
u, err := di.UserService()
....
```

### Generate Mock
dicon's target interfaces are often mocked in unit tests.
So, dicon also provides a tool for automated mock creation.

You just type
```
$ dicon generate-mock --pkg sample
```
then, you get mocks (by the default, under the `mock` package)

```go
// Code generated by "dicon"; DO NOT EDIT.

package mock

type UserRepositoryMock struct {
FindByIdMock func(a0 int64) (*entity.User, error)
}

func NewUserRepositoryMock() *UserRepositoryMock {
return &UserRepositoryMock{}
}

func (mk *UserRepositoryMock) FindById(a0 int64) (*entity.User, error) {
return mk.FindByIdMock(a0)
}

type UserServiceMock struct {
FindMock func(a1 int64) (*entity.User, error)
}

func NewUserServiceMock() *UserServiceMock {
return &UserServiceMock{}
}

func (mk *UserServiceMock) Find(a0 int64) (*entity.User, error) {
return mk.FindMock(a0)
}
```
Generated mocks have `XXXMock` func as a field (XXX is same as interface method name).
In testing, you can freely rewrite behaviors by assigning `func` to this field.
```go
func TestUserService_Find(t *testing.T) {
m := mock.NewUserRepositoryMock()
m.FindByIdMock = func(id int64) (*entity.User, error) {

// mocking logic....

return user, nil
}

service := NewUserService(m) // passing the mock

if _, err := service.Find(id); err != nil {
t.Error(err)
}
}
```

## Options
- generate
```
$ dicon generate -h
NAME:
dicon generate - generate dicon_gen file

USAGE:
dicon generate [command options] [arguments...]

OPTIONS:
--pkg value, -p value target package(s).
--out value, -o value output file name (default: "dicon_gen")
--dry-run
```
- generate mock
```
$ dicon generate-mock -h
NAME:
dicon generate-mock - generate dicon_mock file

USAGE:
dicon generate-mock [command options] [arguments...]

OPTIONS:
--pkg value, -p value target package(s).
--out value, -o value output file name (default: "dicon_mock")
--dist value, -d value output package name (default: "mock")
--dry-run
```

## License
This project is licensed under the Apache License 2.0 License - see the [LICENSE](LICENSE) file for details