From e53a3c3e1fbf2a90dc72d4db769c6f67a78a52cc Mon Sep 17 00:00:00 2001 From: Jonathan Schweder Date: Fri, 18 Aug 2023 14:08:59 +0100 Subject: [PATCH] Add Json generator (#147) Add Json generator --- faker.go | 5 ++++ json.go | 79 ++++++++++++++++++++++++++++++++++++++++++++++++++++ json_test.go | 79 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 163 insertions(+) create mode 100644 json.go create mode 100644 json_test.go diff --git a/faker.go b/faker.go index 65c7062..7102ce3 100644 --- a/faker.go +++ b/faker.go @@ -595,3 +595,8 @@ func NewWithSeed(src rand.Source) (f Faker) { func (f Faker) Blood() Blood { return Blood{&f} } + +// Json returns a fake Json instance for Faker +func (f Faker) Json() Json { + return Json{&f} +} diff --git a/json.go b/json.go new file mode 100644 index 0000000..5e19f59 --- /dev/null +++ b/json.go @@ -0,0 +1,79 @@ +package faker + +import ( + "encoding/json" + "strconv" +) + +// Json is a faker struct for json files +type Json struct { + Faker *Faker +} + +var ( + attributesTypes = []string{"string", "number", "object", "array", "boolean", "null"} + attributesTypesWithoutArrayAndObject = []string{"string", "number", "boolean", "null"} +) + +func (j *Json) randomAttributeValueFromListAsString(validAttributesTypes []string) string { + attributeType := j.Faker.RandomStringElement(validAttributesTypes) + switch attributeType { + case "string": + return "\"" + j.Faker.Lorem().Word() + "\"" + case "number": + number := strconv.Itoa(j.Faker.RandomNumber(j.Faker.RandomDigit())) + return number + case "object": + // Avoid having multiple nested objects by not using object and array as valid attribute types + return j.randomJsonObjectAsString(attributesTypesWithoutArrayAndObject) + case "array": + objects := "" + for i := 0; i < j.Faker.IntBetween(1, 10); i++ { + if objects != "" { + objects += ", " + } + // Avoid having multiple nested objects by not using object and array as valid attribute types + objects += j.randomJsonObjectAsString(attributesTypesWithoutArrayAndObject) + } + return "[" + objects + "]" + case "boolean": + return j.Faker.RandomStringElement([]string{"true", "false"}) + case "null": + return "null" + } + + panic("Invalid attribute type: " + attributeType) +} + +func (j *Json) randomJsonObjectAsString(validAttributesTypes []string) string { + numberAttributes := j.Faker.IntBetween(1, 10) + attributes := make([]string, numberAttributes) + for i := 0; i < numberAttributes; i++ { + attributeName := j.Faker.Lorem().Word() + attributeValue := j.randomAttributeValueFromListAsString(validAttributesTypes) + attributes[i] = "\"" + attributeName + "\": " + attributeValue + } + + result := "{" + for i := 0; i < len(attributes); i++ { + if i > 0 { + result += ", " + } + result += attributes[i] + } + result += "}" + return result +} + +// String generates a random json string +func (j *Json) String() string { + return j.randomJsonObjectAsString(attributesTypes) +} + +// Object generates a random json object +func (j *Json) Object() map[string]interface{} { + result := j.String() + var data map[string]interface{} + json.Unmarshal([]byte(result), &data) + return data +} diff --git a/json_test.go b/json_test.go new file mode 100644 index 0000000..6b07708 --- /dev/null +++ b/json_test.go @@ -0,0 +1,79 @@ +package faker + +import ( + "encoding/json" + "testing" +) + +func TestJsonString(t *testing.T) { + faker := New() + j := faker.Json() + + for i := 0; i < 10; i++ { + result := j.String() + + // Attempt to unmarshal the result into a map[string]interface{} + var data map[string]interface{} + err := json.Unmarshal([]byte(result), &data) + Expect(t, err, nil) + + // Ensure that the result is a valid JSON object + Expect(t, len(data) > 0, true) + + // Ensure that all attribute values are valid JSON types + for _, value := range data { + switch value.(type) { + case string, float64, bool, nil: + // Valid JSON types + case []interface{}: + // Valid JSON array type + case map[string]interface{}: + // Valid JSON object type + default: + t.FailNow() + } + } + } +} + +func TestJsonObject(t *testing.T) { + faker := New() + j := faker.Json() + + for i := 0; i < 10; i++ { + data := j.Object() + + // Ensure that the result is not nil + NotExpect(t, data, nil) + + // Ensure that the result is a valid JSON object + Expect(t, len(data) > 0, true) + + // Ensure that all attribute values are valid JSON types + for _, value := range data { + switch value.(type) { + case string, float64, bool, nil: + // Valid JSON types + case []interface{}: + // Valid JSON array type + case map[string]interface{}: + // Valid JSON object type + default: + t.FailNow() + } + } + } +} + +func TestRandomAttributeValueFromListAsStringPanicWhenUnsupportedTypeIsPassed(t *testing.T) { + faker := New() + j := faker.Json() + + defer func() { + if r := recover(); r == nil { + NotExpect(t, r, nil) + } + }() + + j.randomAttributeValueFromListAsString([]string{"unsupported"}) +}