package faker // Faker is a simple fake data generator for your own struct. // Save your time, and Fake your data for your testing now. import ( "errors" "fmt" "math/rand" "reflect" "regexp" "strconv" "strings" "sync" "time" "github.com/bxcodec/faker/v3/support/slice" ) var ( mu = &sync.Mutex{} // Sets nil if the value type is struct or map and the size of it equals to zero. shouldSetNil = false //Sets random integer generation to zero for slice and maps testRandZero = false //Sets the default number of string when it is created randomly. randomStringLen = 25 //Sets the boundary for random value generation. Boundaries can not exceed integer(4 byte...) nBoundary = numberBoundary{start: 0, end: 100} //Sets the random size for slices and maps. randomSize = 100 // Sets the single fake data generator to generate unique values generateUniqueValues = false // Unique values are kept in memory so the generator retries if the value already exists uniqueValues = map[string][]interface{}{} lang = LangENG ) type numberBoundary struct { start int end int } type langRuneBoundary struct { start rune end rune } // Language rune boundaries here var ( // LangENG is for english language LangENG = langRuneBoundary{65, 122} // LangCHI is for chinese language LangCHI = langRuneBoundary{19968, 40869} // LangRUS is for russian language LangRUS = langRuneBoundary{1025, 1105} ) // Supported tags const ( letterIdxBits = 6 // 6 bits to represent a letter index letterIdxMask = 1< end { return errors.New(ErrStartValueBiggerThanEnd) } nBoundary = numberBoundary{start: start, end: end} return nil } // FakeData is the main function. Will generate a fake data based on your struct. You can use this for automation testing, or anything that need automated data. // You don't need to Create your own data for your testing. func FakeData(a interface{}) error { reflectType := reflect.TypeOf(a) if reflectType.Kind() != reflect.Ptr { return errors.New(ErrValueNotPtr) } if reflect.ValueOf(a).IsNil() { return fmt.Errorf(ErrNotSupportedPointer, reflectType.Elem().String()) } rval := reflect.ValueOf(a) finalValue, err := getValue(a) if err != nil { return err } rval.Elem().Set(finalValue.Elem().Convert(reflectType.Elem())) return nil } // AddProvider extend faker with tag to generate fake data with specified custom algorithm // Example: // type Gondoruwo struct { // Name string // Locatadata int // } // // type Sample struct { // ID int64 `faker:"customIdFaker"` // Gondoruwo Gondoruwo `faker:"gondoruwo"` // Danger string `faker:"danger"` // } // // func CustomGenerator() { // // explicit // faker.AddProvider("customIdFaker", func(v reflect.Value) (interface{}, error) { // return int64(43), nil // }) // // functional // faker.AddProvider("danger", func() faker.TaggedFunction { // return func(v reflect.Value) (interface{}, error) { // return "danger-ranger", nil // } // }()) // faker.AddProvider("gondoruwo", func(v reflect.Value) (interface{}, error) { // obj := Gondoruwo{ // Name: "Power", // Locatadata: 324, // } // return obj, nil // }) // } // // func main() { // CustomGenerator() // var sample Sample // faker.FakeData(&sample) // fmt.Printf("%+v", sample) // } // // Will print // {ID:43 Gondoruwo:{Name:Power Locatadata:324} Danger:danger-ranger} // Notes: when using a custom provider make sure to return the same type as the field func AddProvider(tag string, provider TaggedFunction) error { if _, ok := mapperTag[tag]; ok { return errors.New(ErrTagAlreadyExists) } mapperTag[tag] = provider return nil } func getValue(a interface{}) (reflect.Value, error) { t := reflect.TypeOf(a) if t == nil { return reflect.Value{}, fmt.Errorf("interface{} not allowed") } k := t.Kind() switch k { case reflect.Ptr: v := reflect.New(t.Elem()) var val reflect.Value var err error if a != reflect.Zero(reflect.TypeOf(a)).Interface() { val, err = getValue(reflect.ValueOf(a).Elem().Interface()) if err != nil { return reflect.Value{}, err } } else { val, err = getValue(v.Elem().Interface()) if err != nil { return reflect.Value{}, err } } v.Elem().Set(val.Convert(t.Elem())) return v, nil case reflect.Struct: switch t.String() { case "time.Time": ft := time.Now().Add(time.Duration(rand.Int63())) return reflect.ValueOf(ft), nil default: originalDataVal := reflect.ValueOf(a) v := reflect.New(t).Elem() retry := 0 // error if cannot generate unique value after maxRetry tries for i := 0; i < v.NumField(); i++ { if !v.Field(i).CanSet() { continue // to avoid panic to set on unexported field in struct } tags := decodeTags(t, i) switch { case tags.keepOriginal: zero, err := isZero(reflect.ValueOf(a).Field(i)) if err != nil { return reflect.Value{}, err } if zero { err := setDataWithTag(v.Field(i).Addr(), tags.fieldType) if err != nil { return reflect.Value{}, err } continue } v.Field(i).Set(reflect.ValueOf(a).Field(i)) case tags.fieldType == "": val, err := getValue(v.Field(i).Interface()) if err != nil { return reflect.Value{}, err } val = val.Convert(v.Field(i).Type()) v.Field(i).Set(val) case tags.fieldType == SKIP: item := originalDataVal.Field(i).Interface() if v.CanSet() && item != nil { v.Field(i).Set(reflect.ValueOf(item)) } default: err := setDataWithTag(v.Field(i).Addr(), tags.fieldType) if err != nil { return reflect.Value{}, err } } if tags.unique { if retry >= maxRetry { return reflect.Value{}, fmt.Errorf(ErrUniqueFailure, reflect.TypeOf(a).Field(i).Name) } value := v.Field(i).Interface() if slice.ContainsValue(uniqueValues[tags.fieldType], value) { // Retry if unique value already found i-- retry++ continue } retry = 0 uniqueValues[tags.fieldType] = append(uniqueValues[tags.fieldType], value) } else { retry = 0 } } return v, nil } case reflect.String: res := randomString(randomStringLen, &lang) return reflect.ValueOf(res), nil case reflect.Slice: len := randomSliceAndMapSize() if shouldSetNil && len == 0 { return reflect.Zero(t), nil } v := reflect.MakeSlice(t, len, len) for i := 0; i < v.Len(); i++ { val, err := getValue(v.Index(i).Interface()) if err != nil { return reflect.Value{}, err } val = val.Convert(v.Index(i).Type()) v.Index(i).Set(val) } return v, nil case reflect.Array: v := reflect.New(t).Elem() for i := 0; i < v.Len(); i++ { val, err := getValue(v.Index(i).Interface()) if err != nil { return reflect.Value{}, err } val = val.Convert(v.Index(i).Type()) v.Index(i).Set(val) } return v, nil case reflect.Int: return reflect.ValueOf(randomInteger()), nil case reflect.Int8: return reflect.ValueOf(int8(randomInteger())), nil case reflect.Int16: return reflect.ValueOf(int16(randomInteger())), nil case reflect.Int32: return reflect.ValueOf(int32(randomInteger())), nil case reflect.Int64: return reflect.ValueOf(int64(randomInteger())), nil case reflect.Float32: return reflect.ValueOf(rand.Float32()), nil case reflect.Float64: return reflect.ValueOf(rand.Float64()), nil case reflect.Bool: val := rand.Intn(2) > 0 return reflect.ValueOf(val), nil case reflect.Uint: return reflect.ValueOf(uint(randomInteger())), nil case reflect.Uint8: return reflect.ValueOf(uint8(randomInteger())), nil case reflect.Uint16: return reflect.ValueOf(uint16(randomInteger())), nil case reflect.Uint32: return reflect.ValueOf(uint32(randomInteger())), nil case reflect.Uint64: return reflect.ValueOf(uint64(randomInteger())), nil case reflect.Map: len := randomSliceAndMapSize() if shouldSetNil && len == 0 { return reflect.Zero(t), nil } v := reflect.MakeMap(t) for i := 0; i < len; i++ { keyInstance := reflect.New(t.Key()).Elem().Interface() key, err := getValue(keyInstance) if err != nil { return reflect.Value{}, err } valueInstance := reflect.New(t.Elem()).Elem().Interface() val, err := getValue(valueInstance) if err != nil { return reflect.Value{}, err } v.SetMapIndex(key, val) } return v, nil default: err := fmt.Errorf("no support for kind %+v", t) return reflect.Value{}, err } } func isZero(field reflect.Value) (bool, error) { if field.Kind() == reflect.Map { return field.Len() == 0, nil } for _, kind := range []reflect.Kind{reflect.Struct, reflect.Slice, reflect.Array} { if kind == field.Kind() { return false, fmt.Errorf("keep not allowed on struct") } } return reflect.Zero(field.Type()).Interface() == field.Interface(), nil } func decodeTags(typ reflect.Type, i int) structTag { tags := strings.Split(typ.Field(i).Tag.Get(tagName), ",") keepOriginal := false uni := false res := make([]string, 0) for _, tag := range tags { if tag == keep { keepOriginal = true continue } else if tag == unique { uni = true continue } res = append(res, tag) } return structTag{ fieldType: strings.Join(res, ","), unique: uni, keepOriginal: keepOriginal, } } type structTag struct { fieldType string unique bool keepOriginal bool } func setDataWithTag(v reflect.Value, tag string) error { if v.Kind() != reflect.Ptr { return errors.New(ErrValueNotPtr) } v = reflect.Indirect(v) switch v.Kind() { case reflect.Ptr: if _, exist := mapperTag[tag]; !exist { return fmt.Errorf(ErrTagNotSupported, tag) } if _, def := defaultTag[tag]; !def { res, err := mapperTag[tag](v) if err != nil { return err } v.Set(reflect.ValueOf(res)) return nil } t := v.Type() newv := reflect.New(t.Elem()) res, err := mapperTag[tag](newv.Elem()) if err != nil { return err } rval := reflect.ValueOf(res) newv.Elem().Set(rval) v.Set(newv) return nil case reflect.String: return userDefinedString(v, tag) case reflect.Int, reflect.Int32, reflect.Int64, reflect.Int8, reflect.Int16, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Float32, reflect.Float64: return userDefinedNumber(v, tag) case reflect.Slice, reflect.Array: /** * check for added Provider tag first before * defaulting to userDefinedArray() * this way the user at least has the * option of a custom tag working */ _, tagExists := mapperTag[tag] if tagExists { res, err := mapperTag[tag](v) if err != nil { return err } v.Set(reflect.ValueOf(res)) return nil } return userDefinedArray(v, tag) case reflect.Map: return userDefinedMap(v, tag) default: if _, exist := mapperTag[tag]; !exist { return fmt.Errorf(ErrTagNotSupported, tag) } res, err := mapperTag[tag](v) if err != nil { return err } v.Set(reflect.ValueOf(res)) } return nil } func userDefinedMap(v reflect.Value, tag string) error { if tagFunc, ok := mapperTag[tag]; ok { res, err := tagFunc(v) if err != nil { return err } v.Set(reflect.ValueOf(res)) return nil } len := randomSliceAndMapSize() if shouldSetNil && len == 0 { v.Set(reflect.Zero(v.Type())) return nil } definedMap := reflect.MakeMap(v.Type()) for i := 0; i < len; i++ { key, err := getValueWithTag(v.Type().Key(), tag) if err != nil { return err } val, err := getValueWithTag(v.Type().Elem(), tag) if err != nil { return err } definedMap.SetMapIndex(reflect.ValueOf(key), reflect.ValueOf(val)) } v.Set(definedMap) return nil } func getValueWithTag(t reflect.Type, tag string) (interface{}, error) { switch t.Kind() { case reflect.Int, reflect.Int32, reflect.Int64, reflect.Int8, reflect.Int16, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: res, err := extractNumberFromTag(tag, t) if err != nil { return nil, err } return res, nil case reflect.String: res, err := extractStringFromTag(tag) if err != nil { return nil, err } return res, nil default: return 0, errors.New(ErrUnknownType) } } func userDefinedArray(v reflect.Value, tag string) error { len := randomSliceAndMapSize() if shouldSetNil && len == 0 { v.Set(reflect.Zero(v.Type())) return nil } array := reflect.MakeSlice(v.Type(), len, len) for i := 0; i < len; i++ { res, err := getValueWithTag(v.Type().Elem(), tag) if err != nil { return err } array.Index(i).Set(reflect.ValueOf(res)) } v.Set(array) return nil } func userDefinedString(v reflect.Value, tag string) error { var res interface{} var err error if tagFunc, ok := mapperTag[tag]; ok { res, err = tagFunc(v) if err != nil { return err } } else { res, err = extractStringFromTag(tag) if err != nil { return err } } if res == nil { return fmt.Errorf(ErrTagNotSupported, tag) } val, _ := res.(string) v.SetString(val) return nil } func userDefinedNumber(v reflect.Value, tag string) error { var res interface{} var err error if tagFunc, ok := mapperTag[tag]; ok { res, err = tagFunc(v) if err != nil { return err } } else { res, err = extractNumberFromTag(tag, v.Type()) if err != nil { return err } } if res == nil { return fmt.Errorf(ErrTagNotSupported, tag) } v.Set(reflect.ValueOf(res)) return nil } func extractStringFromTag(tag string) (interface{}, error) { var err error strlen := randomStringLen strlng := &lang if !strings.Contains(tag, Length) && !strings.Contains(tag, Language) { return nil, fmt.Errorf(ErrTagNotSupported, tag) } if strings.Contains(tag, Length) { lenParts := strings.SplitN(findLenReg.FindString(tag), Equals, -1) if len(lenParts) != 2 { return nil, fmt.Errorf(ErrWrongFormattedTag, tag) } strlen, _ = strconv.Atoi(lenParts[1]) } if strings.Contains(tag, Language) { strlng, err = extractLangFromTag(tag) if err != nil { return nil, fmt.Errorf(ErrWrongFormattedTag, tag) } } res := randomString(strlen, strlng) return res, nil } func extractLangFromTag(tag string) (*langRuneBoundary, error) { text := findLangReg.FindString(tag) texts := strings.SplitN(text, Equals, -1) if len(texts) != 2 { return nil, fmt.Errorf(ErrWrongFormattedTag, text) } switch strings.ToLower(texts[1]) { case "eng": return &LangENG, nil case "rus": return &LangRUS, nil case "chi": return &LangCHI, nil default: return &LangENG, nil } } func extractNumberFromTag(tag string, t reflect.Type) (interface{}, error) { if !strings.Contains(tag, BoundaryStart) || !strings.Contains(tag, BoundaryEnd) { return nil, fmt.Errorf(ErrTagNotSupported, tag) } valuesStr := strings.SplitN(tag, comma, -1) if len(valuesStr) != 2 { return nil, fmt.Errorf(ErrWrongFormattedTag, tag) } startBoundary, err := extractNumberFromText(valuesStr[0]) if err != nil { return nil, err } endBoundary, err := extractNumberFromText(valuesStr[1]) if err != nil { return nil, err } boundary := numberBoundary{start: startBoundary, end: endBoundary} switch t.Kind() { case reflect.Uint: return uint(randomIntegerWithBoundary(boundary)), nil case reflect.Uint8: return uint8(randomIntegerWithBoundary(boundary)), nil case reflect.Uint16: return uint16(randomIntegerWithBoundary(boundary)), nil case reflect.Uint32: return uint32(randomIntegerWithBoundary(boundary)), nil case reflect.Uint64: return uint64(randomIntegerWithBoundary(boundary)), nil case reflect.Int: return randomIntegerWithBoundary(boundary), nil case reflect.Int8: return int8(randomIntegerWithBoundary(boundary)), nil case reflect.Int16: return int16(randomIntegerWithBoundary(boundary)), nil case reflect.Int32: return int32(randomIntegerWithBoundary(boundary)), nil case reflect.Int64: return int64(randomIntegerWithBoundary(boundary)), nil default: return nil, errors.New(ErrNotSupportedTypeForTag) } } func extractNumberFromText(text string) (int, error) { text = strings.TrimSpace(text) texts := strings.SplitN(text, Equals, -1) if len(texts) != 2 { return 0, fmt.Errorf(ErrWrongFormattedTag, text) } return strconv.Atoi(texts[1]) } func randomString(n int, lang *langRuneBoundary) string { b := make([]rune, 0) for i := 0; i < n; { randRune := rune(rand.Intn(int(lang.end-lang.start)) + int(lang.start)) b = append(b, randRune) i++ } return string(b) } // randomIntegerWithBoundary returns a random integer between input start and end boundary. [start, end) func randomIntegerWithBoundary(boundary numberBoundary) int { return rand.Intn(boundary.end-boundary.start) + boundary.start } // randomInteger returns a random integer between start and end boundary. [start, end) func randomInteger() int { return rand.Intn(nBoundary.end-nBoundary.start) + nBoundary.start } // randomSliceAndMapSize returns a random integer between [0,randomSliceAndMapSize). If the testRandZero is set, returns 0 // Written for test purposes for shouldSetNil func randomSliceAndMapSize() int { if testRandZero { return 0 } return rand.Intn(randomSize) } func randomElementFromSliceString(s []string) string { return s[rand.Int()%len(s)] } func randomStringNumber(n int) string { b := make([]byte, n) for i, cache, remain := n-1, rand.Int63(), letterIdxMax; i >= 0; { if remain == 0 { cache, remain = rand.Int63(), letterIdxMax } if idx := int(cache & letterIdxMask); idx < len(numberBytes) { b[i] = numberBytes[idx] i-- } cache >>= letterIdxBits remain-- } return string(b) } // RandomInt Get three parameters , only first mandatory and the rest are optional // If only set one parameter : This means the minimum number of digits and the total number // If only set two parameters : First this is min digit and second max digit and the total number the difference between them // If only three parameters: the third argument set Max count Digit func RandomInt(parameters ...int) (p []int, err error) { switch len(parameters) { case 1: minCount := parameters[0] p = rand.Perm(minCount) for i := range p { p[i] += minCount } case 2: minDigit, maxDigit := parameters[0], parameters[1] p = rand.Perm(maxDigit - minDigit + 1) for i := range p { p[i] += minDigit } default: err = fmt.Errorf(ErrMoreArguments, len(parameters)) } return p, err } func generateUnique(dataType string, fn func() interface{}) (interface{}, error) { for i := 0; i < maxRetry; i++ { value := fn() if !slice.ContainsValue(uniqueValues[dataType], value) { // Retry if unique value already found uniqueValues[dataType] = append(uniqueValues[dataType], value) return value, nil } } return reflect.Value{}, fmt.Errorf(ErrUniqueFailure, dataType) } func singleFakeData(dataType string, fn func() interface{}) interface{} { if generateUniqueValues { v, err := generateUnique(dataType, fn) if err != nil { panic(err) } return v } return fn() }