[DEPRECATED] this repository has been moved to: https://github.com/recruit-tech/dicon
Clone or download

README.md

dicon

[DEPRECATED] this repository has been moved to: https://github.com/recruit-tech/dicon

DICONtainer Generator for go.

CircleCI Go Report Card

Getting Started

Prerequisites

  • Go 1.9+
  • make

Installing

$ go get -u github.com/akito0107/dicon

How to use

  1. Write container interface and comment +DICON over it.
// +DICON
type Container interface {
	UserService() (UserService, error)
	UserRepository() (UserRepository, error)
}
  1. 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.
type User struct {
	ID   int64
	Name string
}
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
}
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
}
  1. generate!
$ dicon generate --pkg sample
  1. You can get the container implementation!
// 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
}
  1. Use it!
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)

// 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.

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 file for details