Skip to content

Commit 7bb65c6

Browse files
committed
Merge branch 'develop'
2 parents 13a17db + 272cff4 commit 7bb65c6

16 files changed

+2381
-346
lines changed

BENCHMARKS.md

Lines changed: 455 additions & 173 deletions
Large diffs are not rendered by default.

README.md

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,47 @@ fmt.Println(f.Created.String()) // 1908-12-07 04:14:25.685339029 +0000 UTC
168168
// Nested Struct Fields and Embedded Fields
169169
```
170170

171+
## Fakeable types
172+
173+
It is possible to extend a struct by implementing the `Fakeable` interface
174+
in order to control the generation.
175+
176+
For example, this is useful when it is not possible to modify the struct that you want to fake by adding struct tags to a field but you still need to be able to control the generation process.
177+
178+
```go
179+
// Custom string that you want to generate your own data for
180+
// or just return a static value
181+
type CustomString string
182+
183+
func (c *CustomString) Fake(faker *gofakeit.Faker) interface{} {
184+
return CustomString("my custom string")
185+
}
186+
187+
// Imagine a CustomTime type that is needed to support a custom JSON Marshaler
188+
type CustomTime time.Time
189+
190+
func (c *CustomTime) Fake(faker *gofakeit.Faker) interface{} {
191+
return CustomTime(time.Now())
192+
}
193+
194+
func (c *CustomTime) MarshalJSON() ([]byte, error) {
195+
//...
196+
}
197+
198+
// This is the struct that we cannot modify to add struct tags
199+
type NotModifiable struct {
200+
Token string
201+
Value CustomString
202+
Creation *CustomTime
203+
}
204+
205+
var f NotModifiable
206+
gofakeit.Struct(&f)
207+
fmt.Printf("%s", f.Token) // yvqqdH
208+
fmt.Printf("%s", f.Value) // my custom string
209+
fmt.Printf("%s", f.Creation) // 2023-04-02 23:00:00 +0000 UTC m=+0.000000001
210+
```
211+
171212
## Custom Functions
172213

173214
In a lot of situations you may need to use your own random function usage for your specific needs.
@@ -226,7 +267,10 @@ All functions also exist as methods on the Faker struct
226267

227268
### File
228269

270+
Passing `nil` to `CSV`, `JSON` or `XML` it will auto generate data using a random set of generators.
271+
229272
```go
273+
CSV(co *CSVOptions) ([]byte, error)
230274
JSON(jo *JSONOptions) ([]byte, error)
231275
XML(xo *XMLOptions) ([]byte, error)
232276
FileExtension() string
@@ -533,6 +577,13 @@ BitcoinAddress() string
533577
BitcoinPrivateKey() string
534578
```
535579

580+
### Finance
581+
582+
```go
583+
Cusip() string
584+
Isin() string
585+
```
586+
536587
### Company
537588

538589
```go

csv.go

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,18 +13,28 @@ import (
1313

1414
// CSVOptions defines values needed for csv generation
1515
type CSVOptions struct {
16-
Delimiter string `json:"delimiter" xml:"delimiter"`
17-
RowCount int `json:"row_count" xml:"row_count"`
18-
Fields []Field `json:"fields" xml:"fields"`
16+
Delimiter string `json:"delimiter" xml:"delimiter" fake:"{randomstring:[,,tab]}"`
17+
RowCount int `json:"row_count" xml:"row_count" fake:"{number:1,10}"`
18+
Fields []Field `json:"fields" xml:"fields" fake:"{internal_exampleFields}"`
1919
}
2020

2121
// CSV generates an object or an array of objects in json format
22-
func CSV(co *CSVOptions) ([]byte, error) { return csvFunc(globalFaker.Rand, co) }
22+
// A nil CSVOptions returns a randomly structured CSV.
23+
func CSV(co *CSVOptions) ([]byte, error) { return csvFunc(globalFaker, co) }
2324

2425
// CSV generates an object or an array of objects in json format
25-
func (f *Faker) CSV(co *CSVOptions) ([]byte, error) { return csvFunc(f.Rand, co) }
26+
// A nil CSVOptions returns a randomly structured CSV.
27+
func (f *Faker) CSV(co *CSVOptions) ([]byte, error) { return csvFunc(f, co) }
28+
29+
func csvFunc(f *Faker, co *CSVOptions) ([]byte, error) {
30+
if co == nil {
31+
// We didn't get a CSVOptions, so create a new random one
32+
err := f.Struct(&co)
33+
if err != nil {
34+
return nil, err
35+
}
36+
}
2637

27-
func csvFunc(r *rand.Rand, co *CSVOptions) ([]byte, error) {
2838
// Check delimiter
2939
if co.Delimiter == "" {
3040
co.Delimiter = ","
@@ -74,7 +84,7 @@ func csvFunc(r *rand.Rand, co *CSVOptions) ([]byte, error) {
7484
return nil, errors.New("invalid function, " + field.Function + " does not exist")
7585
}
7686

77-
value, err := funcInfo.Generate(r, &field.Params, funcInfo)
87+
value, err := funcInfo.Generate(f.Rand, &field.Params, funcInfo)
7888
if err != nil {
7989
return nil, err
8090
}
@@ -165,7 +175,8 @@ func addFileCSVLookup() {
165175
}
166176
co.Delimiter = delimiter
167177

168-
csvOut, err := csvFunc(r, &co)
178+
f := &Faker{Rand: r}
179+
csvOut, err := csvFunc(f, &co)
169180
if err != nil {
170181
return nil, err
171182
}

csv_test.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,16 @@ func TestCSVLookup(t *testing.T) {
9999
// t.Fatal(fmt.Sprintf("%s", value.([]byte)))
100100
}
101101

102+
func TestCSVNoOptions(t *testing.T) {
103+
Seed(11)
104+
105+
// if CSVOptions is nil -> get a random CSVOptions
106+
_, err := CSV(nil)
107+
if err != nil {
108+
t.Fatal(err.Error())
109+
}
110+
}
111+
102112
func BenchmarkCSVLookup100(b *testing.B) {
103113
faker := New(0)
104114

fakeable.go

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
package gofakeit
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
"reflect"
7+
)
8+
9+
// Fakeable is an interface that can be implemented by a type to provide a custom fake value.
10+
type Fakeable interface {
11+
// Fake returns a fake value for the type.
12+
Fake(faker *Faker) interface{}
13+
}
14+
15+
func isFakeable(t reflect.Type) bool {
16+
fakeableTyp := reflect.TypeOf((*Fakeable)(nil)).Elem()
17+
18+
return t.Implements(fakeableTyp) || reflect.PtrTo(t).Implements(fakeableTyp)
19+
}
20+
21+
func callFake(faker *Faker, v reflect.Value, possibleKinds ...reflect.Kind) (interface{}, error) {
22+
f, ok := v.Addr().Interface().(Fakeable)
23+
if !ok {
24+
return nil, errors.New("not a Fakeable type")
25+
}
26+
27+
fakedValue := f.Fake(faker)
28+
k := reflect.TypeOf(fakedValue).Kind()
29+
if !containsKind(possibleKinds, k) {
30+
return nil, fmt.Errorf("returned value kind %q is not amongst the valid ones: %v", k, possibleKinds)
31+
}
32+
33+
switch k {
34+
case reflect.String:
35+
return reflect.ValueOf(fakedValue).String(), nil
36+
case reflect.Bool:
37+
return reflect.ValueOf(fakedValue).Bool(), nil
38+
case reflect.Int:
39+
return int(reflect.ValueOf(fakedValue).Int()), nil
40+
case reflect.Int8:
41+
return int8(reflect.ValueOf(fakedValue).Int()), nil
42+
case reflect.Int16:
43+
return int16(reflect.ValueOf(fakedValue).Int()), nil
44+
case reflect.Int32:
45+
return int32(reflect.ValueOf(fakedValue).Int()), nil
46+
case reflect.Int64:
47+
return int64(reflect.ValueOf(fakedValue).Int()), nil
48+
case reflect.Uint:
49+
return uint(reflect.ValueOf(fakedValue).Uint()), nil
50+
case reflect.Uint8:
51+
return uint8(reflect.ValueOf(fakedValue).Uint()), nil
52+
case reflect.Uint16:
53+
return uint16(reflect.ValueOf(fakedValue).Uint()), nil
54+
case reflect.Uint32:
55+
return uint32(reflect.ValueOf(fakedValue).Uint()), nil
56+
case reflect.Uint64:
57+
return uint64(reflect.ValueOf(fakedValue).Uint()), nil
58+
case reflect.Float32:
59+
return float32(reflect.ValueOf(fakedValue).Float()), nil
60+
case reflect.Float64:
61+
return float64(reflect.ValueOf(fakedValue).Float()), nil
62+
case reflect.Slice:
63+
return reflect.ValueOf(fakedValue).Interface(), nil
64+
case reflect.Map:
65+
return reflect.ValueOf(fakedValue).Interface(), nil
66+
case reflect.Struct:
67+
return reflect.ValueOf(fakedValue).Interface(), nil
68+
default:
69+
return nil, fmt.Errorf("unsupported type %q", k)
70+
}
71+
}
72+
73+
func containsKind(possibleKinds []reflect.Kind, kind reflect.Kind) bool {
74+
for _, k := range possibleKinds {
75+
if k == kind {
76+
return true
77+
}
78+
}
79+
return false
80+
}

0 commit comments

Comments
 (0)