我如何在接口内部模拟特定的嵌入式方法

I have this code and I wanna write a unit tests for update function.

how can i mock FindByUsername function ?

I try to overwrite u.FindByUsername but it's doesn't work.

also, I can write some function to give u *UserLogic and userName string as input parameters and execute u.FindByUsername() and mock this function but it's not a clean solution I need a better solution for mocking methods inside UserOperation interface.

package logic

import (
    "errors"
    "fmt"
)

var (
    dataStore = map[string]*User{
        "optic": &User{
            Username: "bla",
            Password: "ola",
        },
    }
)

//UserOperation interface
type UserOperation interface {
    Update(info *User) error
    FindByUsername(userName string) (*User, error)
}

//User struct
type User struct {
    Username string
    Password string
}

//UserLogic struct
type UserLogic struct {
    UserOperation
}

//NewUser struct
func NewUser() UserOperation {
    return &UserLogic{}
}

//Update method
func (u *UserLogic) Update(info *User) error {
    userInfo, err := u.FindByUsername(info.Username)
    if err != nil {
        return err
    }
    fmt.Println(userInfo.Username, userInfo.Password)
    fmt.Println("do some update logic !!!")
    return nil
}

//FindByUsername method
func (u *UserLogic) FindByUsername(userName string) (*User, error) {
    userInfo := &User{}
    var exist bool
    if userInfo, exist = dataStore[userName]; !exist {
        return nil, errors.New("user not found")
    }
    return userInfo, nil
}

Update

I try to mock function with this code

func TestUpdate2(t *testing.T) {
    var MockFunc = func(userName string) (*User, error) {
        return &User{Username:"foo", Password:"bar"},nil
    }
    user := NewUser()
    user.FindByUsername = MockFunc
    user.Update(&User{Username:"optic", Password:"ola"})
}

You're mixing two levels of abstraction in your UserOperation interface: Update depends on FindByUsername. To make Update testable you need to inject the UserFinder functionality into your Update method. You can do this e.g. by defining a field in the UserLogic struct:

type UserOperation interface {
    Update(info *User) error
}

type UserFinder func(userName string) (*User, error)

type UserLogic struct {
    UserOperation
    FindByUsername UserFinder
}

//NewUser struct
func NewUser() *UserLogic { // return structs, accept interfaces!
    return &UserLogic{
        findByUsername: FindByUsername
    }
}

func (u *UserLogic) Update(info *User) error {
    userInfo, err := u.findByUsername(info.Username)
    if err != nil {
        return err
    }
    fmt.Println(userInfo.Username, userInfo.Password)
    fmt.Println("do some update logic !!!")
    return nil
}

func FindByUsername(userName string) (*User, error) {
    userInfo := &User{}
    var exist bool
    if userInfo, exist = dataStore[userName]; !exist {
        return nil, errors.New("user not found")
    }
    return userInfo, nil
}