Skip to content

recruit-tech/dicon

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

92 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

dicon

DICONtainer Generator for go.

CircleCI Go Report Card

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