From 891c02b0cd0874ce9a1e8e53452169d5ea7c85bd Mon Sep 17 00:00:00 2001 From: Arnaud Boussaer Date: Thu, 3 Nov 2022 14:48:06 +0100 Subject: [PATCH 1/2] Add option to only populate simple types --- .../AutoConfigBuilderFixture.cs | 24 ++++++++++++++ src/AutoBogus.Tests/AutoFakerFixture.cs | 23 ++++++++++++++ src/AutoBogus/AutoBinder.cs | 14 ++++++--- src/AutoBogus/AutoConfig.cs | 4 +++ src/AutoBogus/AutoConfigBuilder.cs | 12 +++++++ src/AutoBogus/IAutoConfigBuilder.cs | 12 +++++++ src/AutoBogus/Util/ReflectionHelper.cs | 31 +++++++++++++++++++ 7 files changed, 115 insertions(+), 5 deletions(-) diff --git a/src/AutoBogus.Tests/AutoConfigBuilderFixture.cs b/src/AutoBogus.Tests/AutoConfigBuilderFixture.cs index 0fe6268..88518c0 100644 --- a/src/AutoBogus.Tests/AutoConfigBuilderFixture.cs +++ b/src/AutoBogus.Tests/AutoConfigBuilderFixture.cs @@ -119,6 +119,30 @@ public void Should_Set_Config_TreeDepth_To_Default_If_Null() } } + public class WithOnlySimpleTypes + : AutoConfigBuilderFixture + { + [Fact] + public void Should_Set_Config_OnlySimpleTypes() + { + var onlySimpleTypes = _faker.Random.Bool(); + + _builder.WithOnlySimpleTypes(context => onlySimpleTypes, null); + + _config.OnlySimpleTypes.Invoke(null).Should().Be(onlySimpleTypes); + } + + [Fact] + public void Should_Set_Config_OnlySimpleTypes_To_Default_If_Null() + { + var onlySimpleTypes = AutoConfig.DefaultOnlySimpleTypes.Invoke(null); + + _builder.WithOnlySimpleTypes(null, null); + + _config.OnlySimpleTypes.Invoke(null).Should().Be(onlySimpleTypes); + } + } + public class WithBinder : AutoConfigBuilderFixture { diff --git a/src/AutoBogus.Tests/AutoFakerFixture.cs b/src/AutoBogus.Tests/AutoFakerFixture.cs index 88627da..477dd71 100644 --- a/src/AutoBogus.Tests/AutoFakerFixture.cs +++ b/src/AutoBogus.Tests/AutoFakerFixture.cs @@ -397,6 +397,29 @@ public void Should_Skip_Configured_Members() } } + public class Behaviors_OnlySimpleTypes + : AutoFakerFixture + { + [Fact] + public void Should_Populate_Only_Simple_Types() + { + var instance = AutoFaker.Generate(builder => + { + builder + .WithOnlySimpleTypes(true); + }); + + instance.Id.Should().NotBe(default); + instance.Code.Should().NotBeEmpty(); + instance.DateCreated.Should().NotBe(default); + + instance.Calculator.Should().BeNull(); + instance.Discounts.Should().BeNull(); + instance.Items.Should().BeNull(); + instance.Comments.Should().BeNull(); + } + } + public class Behaviors_Types : AutoFakerFixture { diff --git a/src/AutoBogus/AutoBinder.cs b/src/AutoBogus/AutoBinder.cs index e36df7b..b40ae41 100644 --- a/src/AutoBogus/AutoBinder.cs +++ b/src/AutoBogus/AutoBinder.cs @@ -64,7 +64,7 @@ public virtual void PopulateInstance(object instance, AutoGenerateContext } // Iterate the members and bind a generated value - var autoMembers = GetMembersToPopulate(type, members); + var autoMembers = GetMembersToPopulate(type, members, context); foreach (var member in autoMembers) { @@ -187,17 +187,21 @@ private IAutoGenerator GetParameterGenerator(Type type, ParameterInfo parameter, return AutoGeneratorFactory.GetGenerator(context); } - private IEnumerable GetMembersToPopulate(Type type, IEnumerable members) + private IEnumerable GetMembersToPopulate(Type type, IEnumerable members, AutoGenerateContext context) { + var onlySimpleTypes = context.Config.OnlySimpleTypes.Invoke(context); + // If a list of members is provided, no others should be populated if (members != null) { - return members.Select(member => new AutoMember(member)); + return members.Where(m => !onlySimpleTypes || (m is PropertyInfo info && ReflectionHelper.IsSimple((info).PropertyType))) + .Select(member => new AutoMember(member)); } // Get the baseline members resolved by Bogus - var autoMembers = (from m in GetMembers(type) - select new AutoMember(m.Value)).ToList(); + var autoMembers = GetMembers(type) + .Where(m => !onlySimpleTypes || (m.Value is PropertyInfo info && ReflectionHelper.IsSimple((info).PropertyType))) + .Select(m => new AutoMember(m.Value)).ToList(); foreach (var member in type.GetMembers(BindingFlags)) { diff --git a/src/AutoBogus/AutoConfig.cs b/src/AutoBogus/AutoConfig.cs index 77aa65b..8c7c462 100644 --- a/src/AutoBogus/AutoConfig.cs +++ b/src/AutoBogus/AutoConfig.cs @@ -14,6 +14,7 @@ internal sealed class AutoConfig internal static readonly Func DefaultDataTableRowCount = context => 15; internal static readonly Func DefaultRecursiveDepth = context => 2; internal static readonly Func DefaultTreeDepth = context => null; + internal static readonly Func DefaultOnlySimpleTypes = context => false; internal AutoConfig() { @@ -22,6 +23,7 @@ internal AutoConfig() DataTableRowCount = DefaultDataTableRowCount; RecursiveDepth = DefaultRecursiveDepth; TreeDepth = DefaultTreeDepth; + OnlySimpleTypes = DefaultOnlySimpleTypes; Binder = new AutoBinder(); SkipTypes = new List(); SkipPaths = new List(); @@ -34,6 +36,7 @@ internal AutoConfig(AutoConfig config) RepeatCount = config.RepeatCount; DataTableRowCount = config.DataTableRowCount; RecursiveDepth = config.RecursiveDepth; + OnlySimpleTypes = config.OnlySimpleTypes; TreeDepth = config.TreeDepth; Binder = config.Binder; FakerHub = config.FakerHub; @@ -46,6 +49,7 @@ internal AutoConfig(AutoConfig config) internal Func RepeatCount { get; set; } internal Func DataTableRowCount { get; set; } internal Func RecursiveDepth { get; set; } + internal Func OnlySimpleTypes { get; set; } internal IAutoBinder Binder { get; set; } internal Faker FakerHub { get; set; } internal IList SkipTypes { get; set; } diff --git a/src/AutoBogus/AutoConfigBuilder.cs b/src/AutoBogus/AutoConfigBuilder.cs index 492f522..0ddfd73 100644 --- a/src/AutoBogus/AutoConfigBuilder.cs +++ b/src/AutoBogus/AutoConfigBuilder.cs @@ -25,6 +25,8 @@ internal AutoConfigBuilder(AutoConfig config) IAutoFakerDefaultConfigBuilder IAutoConfigBuilder.WithRecursiveDepth(Func depth) => WithRecursiveDepth(depth, this); IAutoFakerDefaultConfigBuilder IAutoConfigBuilder.WithTreeDepth(int? depth) => WithTreeDepth(context => depth, this); IAutoFakerDefaultConfigBuilder IAutoConfigBuilder.WithTreeDepth(Func depth) => WithTreeDepth(depth, this); + IAutoFakerDefaultConfigBuilder IAutoConfigBuilder.WithOnlySimpleTypes(bool onlySimpleTypes) => WithOnlySimpleTypes(context => onlySimpleTypes, this); + IAutoFakerDefaultConfigBuilder IAutoConfigBuilder.WithOnlySimpleTypes(Func onlySimpleTypes) => WithOnlySimpleTypes(onlySimpleTypes, this); IAutoFakerDefaultConfigBuilder IAutoConfigBuilder.WithBinder(IAutoBinder binder) => WithBinder(binder, this); IAutoFakerDefaultConfigBuilder IAutoConfigBuilder.WithFakerHub(Faker fakerHub) => WithFakerHub(fakerHub, this); IAutoFakerDefaultConfigBuilder IAutoConfigBuilder.WithSkip(Type type) => WithSkip(type, this); @@ -41,6 +43,8 @@ internal AutoConfigBuilder(AutoConfig config) IAutoGenerateConfigBuilder IAutoConfigBuilder.WithRecursiveDepth(Func depth) => WithRecursiveDepth(depth, this); IAutoGenerateConfigBuilder IAutoConfigBuilder.WithTreeDepth(int? depth) => WithTreeDepth(context => depth, this); IAutoGenerateConfigBuilder IAutoConfigBuilder.WithTreeDepth(Func depth) => WithTreeDepth(depth, this); + IAutoGenerateConfigBuilder IAutoConfigBuilder.WithOnlySimpleTypes(bool onlySimpleTypes) => WithOnlySimpleTypes(context => onlySimpleTypes, this); + IAutoGenerateConfigBuilder IAutoConfigBuilder.WithOnlySimpleTypes(Func onlySimpleTypes) => WithOnlySimpleTypes(onlySimpleTypes, this); IAutoGenerateConfigBuilder IAutoConfigBuilder.WithBinder(IAutoBinder binder) => WithBinder(binder, this); IAutoGenerateConfigBuilder IAutoConfigBuilder.WithFakerHub(Faker fakerHub) => WithFakerHub(fakerHub, this); IAutoGenerateConfigBuilder IAutoConfigBuilder.WithSkip(Type type) => WithSkip(type, this); @@ -57,6 +61,8 @@ internal AutoConfigBuilder(AutoConfig config) IAutoFakerConfigBuilder IAutoConfigBuilder.WithRecursiveDepth(Func depth) => WithRecursiveDepth(depth, this); IAutoFakerConfigBuilder IAutoConfigBuilder.WithTreeDepth(int? depth) => WithTreeDepth(context => depth, this); IAutoFakerConfigBuilder IAutoConfigBuilder.WithTreeDepth(Func depth) => WithTreeDepth(depth, this); + IAutoFakerConfigBuilder IAutoConfigBuilder.WithOnlySimpleTypes(bool onlySimpleTypes) => WithOnlySimpleTypes(context => onlySimpleTypes, this); + IAutoFakerConfigBuilder IAutoConfigBuilder.WithOnlySimpleTypes(Func onlySimpleTypes) => WithOnlySimpleTypes(onlySimpleTypes, this); IAutoFakerConfigBuilder IAutoConfigBuilder.WithBinder(IAutoBinder binder) => WithBinder(binder, this); IAutoFakerConfigBuilder IAutoConfigBuilder.WithFakerHub(Faker fakerHub) => WithFakerHub(fakerHub, this); IAutoFakerConfigBuilder IAutoConfigBuilder.WithSkip(Type type) => WithSkip(type, this); @@ -95,6 +101,12 @@ internal TBuilder WithTreeDepth(Func depth, return builder; } + internal TBuilder WithOnlySimpleTypes(Func onlySimpleTypes, TBuilder builder) + { + Config.OnlySimpleTypes = onlySimpleTypes ?? AutoConfig.DefaultOnlySimpleTypes; + return builder; + } + internal TBuilder WithBinder(IAutoBinder binder, TBuilder builder) { Config.Binder = binder ?? new AutoBinder(); diff --git a/src/AutoBogus/IAutoConfigBuilder.cs b/src/AutoBogus/IAutoConfigBuilder.cs index dfa4c19..f942578 100644 --- a/src/AutoBogus/IAutoConfigBuilder.cs +++ b/src/AutoBogus/IAutoConfigBuilder.cs @@ -73,6 +73,18 @@ public interface IAutoConfigBuilder /// The current configuration builder instance. TBuilder WithTreeDepth(Func depth); + /// + /// Registers if only simple type members should be populated with values + /// + /// The current configuration builder instance. + TBuilder WithOnlySimpleTypes(bool onlySimpleTypes); + + /// + /// Registers if only simple type members should be populated with values + /// + /// The current configuration builder instance. + TBuilder WithOnlySimpleTypes(Func onlySimpleTypes); + /// /// Registers a binder instance to use when generating values. /// diff --git a/src/AutoBogus/Util/ReflectionHelper.cs b/src/AutoBogus/Util/ReflectionHelper.cs index a9865b0..f65018c 100644 --- a/src/AutoBogus/Util/ReflectionHelper.cs +++ b/src/AutoBogus/Util/ReflectionHelper.cs @@ -90,6 +90,37 @@ internal static bool IsProperty(MemberInfo member) #endif } + /// + /// Return true if the type is a primitive type, date, decimal, string, or GUID + /// + /// Type for which to check if it is a simple type + internal static bool IsSimple(Type type) + { + if (type == null) + return false; + + var typeInfo = type.GetTypeInfo(); + if (typeInfo.IsGenericType && typeInfo.GetGenericTypeDefinition() == typeof(Nullable<>)) + { + // nullable type, check if the nested type is simple. +#if NETSTANDARD1_3 + var genericArgs = type.GetTypeInfo().IsGenericTypeDefinition + ? type.GetTypeInfo().GenericTypeParameters + : type.GetTypeInfo().GenericTypeArguments; + return IsSimple(genericArgs[0]); +#else + return IsSimple(typeInfo.GetGenericArguments()[0]); +#endif + } + return typeInfo.IsPrimitive + || typeInfo.IsEnum + || type.Equals(typeof(string)) + || type.Equals(typeof(decimal)) + || type.Equals(typeof(Guid)) + || type.Equals(typeof(DateTime)) + || type.Equals(typeof(DateTimeOffset)); + } + internal static IEnumerable GetGenericArguments(Type type) { return type.GetGenericArguments(); From c1619583ae86d92b503c73a82c08f28ec9dfe656 Mon Sep 17 00:00:00 2001 From: Arnaud Boussaer Date: Thu, 3 Nov 2022 14:55:47 +0100 Subject: [PATCH 2/2] code cleanup --- src/AutoBogus/AutoBinder.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/AutoBogus/AutoBinder.cs b/src/AutoBogus/AutoBinder.cs index b40ae41..5ce63fa 100644 --- a/src/AutoBogus/AutoBinder.cs +++ b/src/AutoBogus/AutoBinder.cs @@ -194,13 +194,13 @@ private IEnumerable GetMembersToPopulate(Type type, IEnumerable !onlySimpleTypes || (m is PropertyInfo info && ReflectionHelper.IsSimple((info).PropertyType))) + return members.Where(m => !onlySimpleTypes || (m is PropertyInfo info && ReflectionHelper.IsSimple(info.PropertyType))) .Select(member => new AutoMember(member)); } // Get the baseline members resolved by Bogus var autoMembers = GetMembers(type) - .Where(m => !onlySimpleTypes || (m.Value is PropertyInfo info && ReflectionHelper.IsSimple((info).PropertyType))) + .Where(m => !onlySimpleTypes || (m.Value is PropertyInfo info && ReflectionHelper.IsSimple(info.PropertyType))) .Select(m => new AutoMember(m.Value)).ToList(); foreach (var member in type.GetMembers(BindingFlags))