How to Test mapped Objects in golang - go

I try to implement some unit testing in my go code and find the topic of mocking method quite difficult.
I have the following example where I hope you can help me :)
On the first layer I have the following code:
package api
import (
"fmt"
"core"
)
type createUserDTO struct {
Id string
}
func ApiMethod() {
fmt.Println("some incoming api call wit user")
incomingUserData := &createUserDTO{Id: "testId"}
mapedUser := incomingUserData.mapUser()
mapedUser.Create()
}
func (createUserDTO *createUserDTO) mapUser() core.User {
return &core.UserCore{Id: createUserDTO.Id}
}
The second layer has the following code:
package core
import (
"fmt"
)
type CoreUser struct{ Id string }
type User interface {
Create()
}
func (user CoreUser) Create() {
fmt.Println("Do Stuff")
}
My question now is, how do I test every public method in the api package without testing the core package. Especially the method Create().

Based on the comments, I put together a trivial GitHub repository to show how I usually deal with structuring projects in Go. The repository doesn't take into consideration the test part for now but it should be pretty easy to insert them with this project structure.
Let's start with the general folders' layout:
controllers
services
db
dto
models
Now, let's see the relevant files.
models/user.go
package models
import "gorm.io/gorm"
type User struct {
*gorm.Model
Id string `gorm:"primaryKey"`
}
func NewUser(id string) *User {
return &User{Id: id}
}
Simple struct definition here.
dto/user.go
package dto
import "time"
type UserDTO struct {
Id string `json:"id"`
AddedOn time.Time `json:"added_on"`
}
func NewUserDTO(id string) *UserDTO {
return &UserDTO{Id: id}
}
We enrich the model struct with a dummy AddedOn field which needs only for the sake of the demo.
db/user.go
package db
import (
"gorm.io/gorm"
"userapp/models"
)
type UserDb struct {
Conn *gorm.DB
}
type UserDbInterface interface {
SaveUser(user *models.User) error
}
func (u *UserDb) SaveUser(user *models.User) error {
if dbTrn := u.Conn.Create(user); dbTrn.Error != nil {
return dbTrn.Error
}
return nil
}
Here, we define an interface for using the User repository. In our tests, we can provide a mock instead of an instance of the UserDb struct.
services/user.go
package services
import (
"time"
"userapp/db"
"userapp/dto"
"userapp/models"
)
type UserService struct {
DB db.UserDbInterface
}
type UserServiceInterface interface {
AddUser(inputReq *dto.UserDTO) (*dto.UserDTO, error)
}
func NewUserService(db db.UserDbInterface) *UserService {
return &UserService{
DB: db,
}
}
func (u *UserService) AddUser(inputReq *dto.UserDTO) (*dto.UserDTO, error) {
// here you can write complex logic
user := models.NewUser(inputReq.Id)
// invoke db repo
if err := u.DB.SaveUser(user); err != nil {
return nil, err
}
inputReq.AddedOn = time.Now()
return inputReq, nil
}
This is the layer that bridges connections between the presentation layer and the underlying repositories.
controllers/user.go
package controllers
import (
"encoding/json"
"io"
"net/http"
"userapp/dto"
"userapp/services"
)
type UserController struct {
US services.UserServiceInterface
}
func NewUserController(userService services.UserServiceInterface) *UserController {
return &UserController{
US: userService,
}
}
func (u *UserController) Save(w http.ResponseWriter, r *http.Request) {
reqBody, err := io.ReadAll(r.Body)
if err != nil {
panic(err)
}
r.Body.Close()
var userReq dto.UserDTO
json.Unmarshal(reqBody, &userReq)
userRes, err := u.US.AddUser(&userReq)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
json.NewEncoder(w).Encode(err)
return
}
w.WriteHeader(http.StatusCreated)
json.NewEncoder(w).Encode(userRes)
}
Here, we defined the controller that (through Dependency Injection) uses the UserService struct.
You can find everything in my repository on GitHub
Let me know if it clarifies a little bit.

Related

Is this dependecy injection pattern thread safe?

I'm having a hard time coming up with a clean pattern to inject dependencies in a REST server that allows me to write isolated unit tests. The below structure seems to work but I'm not sure if it's thread safe.
store:
package store
type InterfaceStore interface {
Connect()
Disconnect()
User() interfaceUser
}
// Wiring up
type store struct {
db *mongo.Database
}
func (s *store) Connect() {
client, err := mongo.Connect()
if err != nil {
log.Fatal(err.Error())
}
s.db = client.Database()
}
func (s *store) Disconnect() {
s.db.Client().Disconnect(context.TODO())
}
func (s *store) User() interfaceUser {
return &user{s.db}
}
// Exposed from the package to create a store instance
func GetStore() InterfaceStore {
return &store{}
}
// User related
type interfaceUser interface {
InsertOne(models.User) (string, error)
}
type user struct {
db *mongo.Database
}
func (u *user) InsertOne(user models.User) (primitive.ObjectID, error) {
collection := u.db.Collection(collectionUsers)
// persisting user in DB
}
server:
package server
type server struct{}
func (s *server) Start() {
storeInstance := store.GetStore()
storeInstance.Connect()
defer storeInstance.Disconnect()
r := gin.Default()
keys := keys.GetKeys()
routes.InitRoutes(r, storeInstance)
port := fmt.Sprintf(":%s", keys.PORT)
r.Run(port)
}
func CreateInstance() *server {
return &server{}
}
routes:
package routes
func InitRoutes(router *gin.Engine, store store.InterfaceStore) {
router.Use(middlewares.Cors)
// createSubrouter creates a Gin routerGroup with the prefix "/user"
userRoutes(createSubrouter("/user", router), store)
}
func userRoutes(router *gin.RouterGroup, store store.InterfaceStore) {
controller := controllers.GetUserController(store)
router.GET("/", controller.Get)
}
controllers:
package controllers
type userControllers struct {
UserService services.InterfaceUser
}
func (u *userControllers) Get(c *gin.Context) {
userDetails, _ := u.UserService.FetchAllInformation(bson.M{"_id": userData.(models.User).ID})
utils.RespondWithJSON(c, userDetails)
}
func GetUserController(store store.InterfaceStore) userControllers {
userService := services.GetUserService(store)
return userControllers{
UserService: &userService,
}
}
services:
package services
type InterfaceUser interface {
FetchAllInformation(bson.M) (*models.User, error)
}
type user struct {
store store.InterfaceStore
}
func (u *user) FetchAllInformation(filter bson.M) (*models.User, error) {
user, err := u.store.User().FindOne(filter)
if err != nil {
return nil, err
}
return user, nil
}
func GetUserService(store store.InterfaceStore) user {
return user{
store: store,
}
}
By using interfaces I'm able to mock the entire service when writing tests for the controller and I can mock the entire store to test the service component without hitting the DB.
I'm wondering if the store instance is safely shared across the code because the interfaces are no pointers. Does that mean a copy of the store is created every time I pass it down the tree?
The type user struct {} definition states store is anything that implements the store.InterfaceStore interface.
If you look carefully, you're implementing it with pointer receivers. That means the (instance pointed by the) receiver will be shared.
If your mock implements them over the value-type it will be copied on method call and you'll be safe, but it will also mean this mock won't be holding new state after the method calls, which I guess is not what you want.
Bottom line, it's not really about how you defined it in the struct, by value or by reference, but what the methods accept as receiver.

Can golang plugins be used for factory functions?

I have the following code in a golang plugin module:
plug.go
package main
import "fmt"
var (
Thing = New("first thing")
ThingFactory = thingFactory{}
)
type thing struct {
i int
s string
}
func New(s string) thing {
return thing{s: s}
}
func (t *thing) Say() string {
t.i++
return fmt.Sprintf("%s - %d", t.s, t.i)
}
type thingFactory struct{}
func (t thingFactory) Make(s string) thing {
return New(s)
}
it is compiled as a .so object and used in another program:
main.go
package main
import (
"fmt"
"plugin"
)
func main() {
p, err := plugin.Open("../plug/plug.so")
if err != nil {
panic(err)
}
symbol, err := p.Lookup("Thing")
if err != nil {
panic(err)
}
thing := symbol.(Sayer)
fmt.Println(thing.Say())
symbol, err = p.Lookup("ThingFactory") // <-problems start here
if err != nil {
panic(err)
}
factory := symbol.(GetSayer)
madeThing := factory.Make("how about me?")
fmt.Println(madeThing.Say())
fmt.Println(madeThing.Say())
}
type Sayer interface {
Say() string
}
type GetSayer interface {
Make(string) Sayer
}
I'm able to lookup the Thing, and call Say() on it, but the second interface conversion panics:
first thing - 1
panic: interface conversion: *main.thingFactory is not main.GetSayer: missing method Make
even though the runtime recognizes the first symbol as a Sayer it doesn't recognize that thingFactory obviously has a Make() method, which should return something that is also a Sayer.
Am I missing something obvious here?
The first problem is that in your plugin thingFactory (more precicely *thingfactory) does not have a method described in your main app's GetSayer interface:
Make(string) Sayer
You have:
Make(string) thing
So (first) you have to change thingFactory.Make() to this:
type Sayer interface {
Say() string
}
func (t thingFactory) Make(s string) Sayer {
th := New(s)
return &th
}
After this it still won't work. And the reason for this is because the plugin's Sayer type is not identical to your main app's Sayer type. But they must be the same in order to implement your main app's GetSayer interface.
One solution is to "outsource" the Sayer interface to its own package, and use this common, shared package both in the plugin and in the main app.
Let's create a new package, call it subplay:
package subplay
type Sayer interface {
Say() string
}
Import this package and use it in the plugin:
package main
import (
"fmt"
"path/to/subplay"
)
var (
Thing = New("first thing")
ThingFactory = thingFactory{}
)
type thing struct {
i int
s string
}
func New(s string) thing {
return thing{s: s}
}
func (t *thing) Say() string {
t.i++
return fmt.Sprintf("%s - %d", t.s, t.i)
}
type thingFactory struct{}
func (t thingFactory) Make(s string) subplay.Sayer {
th := New(s)
return &th
}
And also import and use it in the main app:
package main
import (
"fmt"
"path/to/subplay"
"plugin"
)
func main() {
p, err := plugin.Open("../plug/plug.so")
if err != nil {
panic(err)
}
symbol, err := p.Lookup("Thing")
if err != nil {
panic(err)
}
thing := symbol.(subplay.Sayer)
fmt.Println(thing.Say())
symbol, err = p.Lookup("ThingFactory")
if err != nil {
panic(err)
}
factory := symbol.(GetSayer)
madeThing := factory.Make("how about me?")
fmt.Println(madeThing.Say())
fmt.Println(madeThing.Say())
}
type GetSayer interface {
Make(string) subplay.Sayer
}
Now it will work, and output will be:
first thing - 1
how about me? - 1
how about me? - 2
See related questions:
go 1.8 plugin use custom interface
How do Go plugin dependencies work?
Your plugin Make method should return a Sayer object not thing
type Sayer interface {
Say() string
}
func (t *thingFactory) Make(s string) Sayer {
return New(s)
}

Dependency injection with interface in go

I have a struct StructDependingOnInterface which depends on MyInterface interface. I'd like the struct Implementation to be injected into bar when StructDependingOnInterface is instantiated.
I have tried doing this with facebookgo/inject library, but it seems like it doesn't work with interfaces. s.bar is always nil.
package main
import (
"fmt"
"log"
"github.com/facebookgo/inject"
)
type MyInterface interface {
Foo()
}
type Implementation struct{}
func (imp *Implementation) Foo() {
fmt.Println("Hello")
}
type StructDependingOnInterface struct {
bar MyInterface `inject:""`
}
func main() {
var g inject.Graph
err := g.Provide(
&inject.Object{Value: &Implementation{}},
)
if err != nil {
log.Fatal(err)
}
g.Populate()
s := &StructDependingOnInterface{}
s.bar.Foo()
}
Does the language go allows what I'm trying to do ?
Is there an alternative of facebookgo/inject that would fits my need ?
facebookgo/inject should be able to handle interfaces, look at the example in the link and how they use http.RoundTripper and http.DefaultTransport.
First you need to export bar, i.e. changing it to Bar. Then you also need to pass s to Provide for g.Populate to then be able to set Bar.
type StructDependingOnInterface struct {
Bar MyInterface `inject:""`
}
func main() {
var g inject.Graph
s := &StructDependingOnInterface{}
err := g.Provide(
&inject.Object{Value: s},
&inject.Object{Value: &Implementation{}},
)
if err != nil {
log.Fatal(err)
}
if err := g.Populate(); err != nil {
log.Fatal(err)
}
s.bar.Foo()
}
hiboot is an cli/web framework that support dependency injection written in Go.
Code: https://github.com/hidevopsio/hiboot
Hiboot Overview
Web MVC (Model-View-Controller).
Auto Configuration, pre-create instance with properties configs for dependency injection.
Dependency injection with struct tag name inject:"" or Constructor func.
Sample Code:
package main
import (
"github.com/hidevopsio/hiboot/pkg/app/web"
"github.com/hidevopsio/hiboot/pkg/model"
"github.com/hidevopsio/hiboot/pkg/starter/jwt"
"time"
)
// This example shows that token is injected through constructor,
// once you imported "github.com/hidevopsio/hiboot/pkg/starter/jwt",
// token jwt.Token will be injectable.
func main() {
// create new web application and run it
web.NewApplication().Run()
}
// PATH: /login
type loginController struct {
web.Controller
token jwt.Token
}
type userRequest struct {
// embedded field model.RequestBody mark that userRequest is request body
model.RequestBody
Username string `json:"username" validate:"required"`
Password string `json:"password" validate:"required"`
}
func init() {
// Register Rest Controller through constructor newLoginController
web.RestController(newLoginController)
}
// inject token through the argument token jwt.Token on constructor newLoginController, you may wonder why is token come from
// well, it provided by hiboot starter jwt, see above hiboot link for more details
func newLoginController(token jwt.Token) *loginController {
return &loginController{
token: token,
}
}
// Post /
// The first word of method is the http method POST, the rest is the context mapping
func (c *loginController) Post(request *userRequest) (response model.Response, err error) {
jwtToken, _ := c.token.Generate(jwt.Map{
"username": request.Username,
"password": request.Password,
}, 30, time.Minute)
response = new(model.BaseResponse)
response.SetData(jwtToken)
return
}

Overcoming import cycle not allowed in Go

I understand the problem, as per the answer here, however, I could really use help or a more detailed code explanation of how it's overcome.
My situation is this: I used to have models and controllers separated, and in my models package I had a datastore.go file containing an interface of all the model functions:
package models
type DSDatabase interface {
CreateUser(ctx context.Context, username string, password []byte) (*datastore.Key, error)
// More model functions
}
type datastoreDB struct {
client *datastore.Client
}
var (
DB DSDatabase
_ DSDatabase = &datastoreDB{}
)
func init() {
// init datastore
}
This was all fine because the model functions were also located within the models package, so my functions in the controller package could freely call models.DB.CreateUser(ctx, "username", []byte("password")).
Now, I have decided to move all the above code to a datastore package, whereas the model for CreateUser is located in a user package. In other words, package user now contains both controller and model functions, for which the controller related functions rely on datastore package, while the DSDatabase interface rely on the user model functions.
I would really appreciate help figuring out how to overcome the import cycle while keeping the DSDatastore interface separate from all the other packages such as home and user.
in case the above is not clear enough, the above code has changed to:
package datastore
import (
"github.com/username/projectname/user"
)
type DSDatabase interface {
user.CreateUser(ctx context.Context, username string, passwoUserRegister(ctx context.Context, username string, password []byte) (*datastore.Key, error)
}
...
while in my user package I have this in a controller-related file:
package user
import (
"github.com/username/projectname/datastore"
)
func CreateUserPOST(w http.ResponseWriter, r *http.Request) {
// get formdata and such
datastore.DB.CreateUser(ctx, "username", []byte("password"))
}
and in another model-related file I have:
package user
import (
"github.com/username/projectname/datastore"
)
func (db *datastore.datastoreDB) CreateUser(ctx context.Context, username string) (*User, error) {
key := datastore.NameKey("User", username, nil)
var user User
err := db.client.Get(ctx, key, &user)
if err != nil {
return nil, err
}
return &user, nil
}
Which of course results in an import cycle, that I sadly can't figure out how to overcome..
First things first, you cannot define a method, in pacakge A, on a type declared in package B.
So this...
package user
import (
"github.com/username/projectname/datastore"
)
func (db *datastore.datastoreDB) CreateUser(ctx context.Context, username string) (*User, error) {
key := datastore.NameKey("User", username, nil)
var user User
err := db.client.Get(ctx, key, &user)
if err != nil {
return nil, err
}
return &user, nil
}
...should not even compile.
This here...
package datastore
import (
"github.com/username/projectname/user"
)
type DSDatabase interface {
user.CreateUser(ctx context.Context, username string, passwoUserRegister(ctx context.Context, username string, password []byte) (*datastore.Key, error)
}
...this is also invalid Go code.
As to your question... one thing you could do is to define the Datastore interface inside the user package and have the implementation live in another package, this lends itself nicely for when you need different implementations of one interface. If you do this your user package does not need to know about the datastore package anymore, the datastore package still has to know about the user package though, which is a OK.
An example:
package user
import (
"context"
)
type DSDatabase interface {
CreateUser(ctx context.Context, username string, password []byte) (*User, error)
// ...
}
// This can be set by the package that implements the interface
// or by any other package that imports the user package and
// a package that defines an implementation of the interface.
var DB DSDatabase
type User struct {
// ...
}
func CreateUserPOST(w http.ResponseWriter, r *http.Request) {
// get formdata and such
DB.CreateUser(ctx, "username", []byte("password"))
}
The package with the implementation of the interface:
package datastore
import (
"context"
"github.com/username/projectname/user"
)
// DB implements the user.DSDatabase interface.
type DB struct { /* ... */ }
func (db *DB) CreateUser(ctx context.Context, username string) (*user.User, error) {
key := datastore.NameKey("User", username, nil)
var user user.User
err := db.client.Get(ctx, key, &user)
if err != nil {
return nil, err
}
return &user, nil
}
func init() {
// make sure to initialize the user.DB variable that
// is accessed by the CreateUserPOST func or else you'll
// get nil reference panic.
user.DB = &DB{}
}

How to implement dependency injection in Go

I'm porting an app from Play (Scala) to Go and wondering how to implement dependency injection. In Scala I used the cake pattern, while in Go I implemented a DAO interface along with an implementation for Mongo.
Here below is how I tried to implement a pattern that let me change the DAO implementation as needed (e.g. test, different DB, etc.):
1. entity.go
package models
import (
"time"
"gopkg.in/mgo.v2/bson"
)
type (
Entity struct {
Id bson.ObjectId `json:"id,omitempty" bson:"_id,omitempty"`
CreatedAt time.Time `json:"createdAt,omitempty" bson:"createdAt,omitempty"`
LastUpdate time.Time `json:"lastUpdate,omitempty" bson:"lastUpdate,omitempty"`
}
)
2. user.go
package models
import (
"time"
)
type (
User struct {
Entity `bson:",inline"`
Name string `json:"name,omitempty" bson:"name,omitempty"`
BirthDate time.Time `json:"birthDate,omitempty" bson:"birthDate,omitempty"`
}
)
3. dao.go
package persistence
type (
DAO interface {
Insert(entity interface{}) error
List(result interface{}, sort string) error
Find(id string, result interface{}) error
Update(id string, update interface{}) error
Remove(id string) error
Close()
}
daoFactory func() DAO
)
var (
New daoFactory
)
4. mongoDao.go (DB info and collection name are hard-coded since it's just an example)
package persistence
import (
"fmt"
"time"
"errors"
"gopkg.in/mgo.v2"
"gopkg.in/mgo.v2/bson"
"github.com/fatih/structs"
"cmd/server/models"
)
type (
mongoDAO struct{
session *mgo.Session
}
)
func NewMongoDAO() DAO {
dialInfo := &mgo.DialInfo{
Addrs: []string{"localhost:27017"},
Timeout: 60 * time.Second,
Database: "test",
}
session, err := mgo.DialWithInfo(dialInfo)
if err != nil {
panic(err)
}
session.SetMode(mgo.Monotonic, true)
return &mongoDAO{session}
}
func (dao *mongoDAO) Insert(entity interface{}) error {
doc := entity.(*models.User)
doc.Id = bson.NewObjectId()
doc.CreatedAt = time.Now().UTC()
doc.LastUpdate = time.Now().UTC()
return dao.session.DB("test").C("users").Insert(doc)
}
func (dao *mongoDAO) List(result interface{}, sort string) error {
return dao.session.DB("test").C("users").Find(nil).Sort(sort).All(result)
}
func (dao *mongoDAO) Find(id string, result interface{}) error {
if !bson.IsObjectIdHex(id) {
return errors.New(fmt.Sprintf("%s is not a valid hex id", id))
}
oid := bson.ObjectIdHex(id)
return dao.session.DB("test").C("users").FindId(oid).One(result)
}
func (dao *mongoDAO) Update(id string, update interface{}) error {
if !bson.IsObjectIdHex(id) {
return errors.New(fmt.Sprintf("%s is not a valid hex id", id))
}
oid := bson.ObjectIdHex(id)
doc := update.(*models.User)
doc.LastUpdate = time.Now().UTC()
return dao.session.DB("test").C("users").Update(oid, bson.M{"$set": structs.Map(update)})
}
func (dao *mongoDAO) Remove(id string) error {
if !bson.IsObjectIdHex(id) {
return errors.New(fmt.Sprintf("%s is not a valid hex id", id))
}
oid := bson.ObjectIdHex(id)
return dao.session.DB("test").C("users").RemoveId(oid)
}
func (dao *mongoDAO) Close() {
dao.session.Close()
}
func init() {
New = NewMongoDAO
}
Finally, here is how I use the types above:
5. userController.go
package controllers
import (
"net/http"
"github.com/labstack/echo"
"cmd/server/models"
"cmd/server/persistence"
)
type (
UserController struct {
dao persistence.DAO
}
)
func NewUserController(dao persistence.DAO) *UserController {
return &UserController{dao}
}
func (userController *UserController) CreateUser() echo.HandlerFunc {
return func(context echo.Context) error {
user := &models.User{}
if err := context.Bind(user); err != nil {
return err
}
if err := userController.dao.Insert(user); err != nil {
return err
}
return context.JSON(http.StatusCreated, user)
}
}
func (userController *UserController) UpdateUser() echo.HandlerFunc {
return func(context echo.Context) error {
user := &models.User{}
if err := context.Bind(user); err != nil {
return err
}
id := context.Param("id")
if err := userController.dao.Update(id, user); err != nil {
return err
}
return context.JSON(http.StatusOK, user)
}
}
....
The code above is 90% fine... I've just a problem in mongoDao.go with methods Insert and Update where the compiler forces me to cast input entity to a specific type (*models.User), but this prevents me from having a generic DAO component that works for all types. How do I fix this issue?
How about creating an interface that you implement for the Entity struct?
type Entitier interface {
GetEntity() *Entity
}
The implementation would simply return a pointer to itself that you can now use in the Insert and Update methods of your DAO. This would also have the added benefit of letting you be more specific in the declarations of your DAO methods. Instead of simply stating that they take an arbitrary interface{} as argument you could now say that they take an Entitier.
Like so:
func (dao *mongoDAO) Update(id string, update Entitier) error
Here's a minimal complete example of what I mean:
http://play.golang.org/p/lpVs_61mfM
Hope this gives you some ideas! You might want to adjust naming of Entity/Entitier/GetEntity for style and clarity once you've settled on the pattern to use.
This generalization
DAO interface {
Insert(entity interface{}) error
looks over-helming
You both assert to *models.User for mongo
doc := entity.(*models.User)
and do
user := &models.User{}
userController.dao.Insert(user)
when use your generic DAO interface.
Why don't you just define interface more precisely?
DAO interface {
Insert(entity *models.User) error

Resources