Skip to content

Commit

Permalink
Better Collection and ReadOnlyCollection support
Browse files Browse the repository at this point in the history
New generators, additional tests
  • Loading branch information
soenneker committed Apr 19, 2024
1 parent 6f659df commit 852f891
Show file tree
Hide file tree
Showing 16 changed files with 280 additions and 80 deletions.
26 changes: 19 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@

This project is an automatic creator and populator for the fake data generator [Bogus](https://github.com/bchavez/Bogus). It's a replacement for the abandoned [AutoBogus](https://github.com/nickdodd79/AutoBogus) library.

The goals are to be *fast*, and support the latest types in .NET. It uses the fastest .NET Reflection cache: [soenneker.reflection.cache](https://github.com/soenneker/soenneker.reflection.cache).
The goals:
- Be *fast*
- Support the latest types in .NET

It uses the fastest .NET Reflection cache: [soenneker.reflection.cache](https://github.com/soenneker/soenneker.reflection.cache). Bogus updates are automatically integrated.

.NET 6+ is supported.

Expand Down Expand Up @@ -95,14 +99,21 @@ var order = autoFaker.Generate();

## Benchmarks

### Soenneker.Utils.AutoBogus
### Soenneker.Utils.AutoBogus - `AutoFaker`

| Method | Mean | Error | StdDev |
|----------------- |------------:|----------:|----------:|
| Generate_int | 79.40 ns | 0.635 ns | 0.563 ns |
| Generate_string | 241.35 ns | 3.553 ns | 3.324 ns |
| Generate_complex | 6,782.34 ns | 43.811 ns | 38.837 ns |

### Soenneker.Utils.AutoBogus - `AutoFaker<T>`

| Method | Mean | Error | StdDev |
|----------------- |-----------:|---------:|---------:|
| Generate_string | 283.6 ns | 3.28 ns | 3.07 ns |
| Generate_complex | 8,504.0 ns | 76.58 ns | 67.89 ns |

### AutoBogus

| Method | Mean | Error | StdDev |
Expand All @@ -112,8 +123,9 @@ var order = autoFaker.Generate();

### Bogus

| Method | Mean | Error | StdDev |
|------------- |--------------:|--------------:|-------------:|
| Bogus_int | 19.58 ns | 0.150 ns | 0.133 ns |
| Bogus_string | 172.25 ns | 2.510 ns | 2.347 ns |
| Bogus_ctor | 717,799.56 ns | 10,086.875 ns | 9,435.269 ns |
| Method | Mean | Error | StdDev |
|------------- |--------------:|-------------:|-------------:|
| Bogus_int | 19.70 ns | 0.176 ns | 0.165 ns |
| Bogus_string | 171.75 ns | 2.763 ns | 2.585 ns |
| Bogus_ctor | 730,669.06 ns | 8,246.622 ns | 7,310.416 ns |

6 changes: 2 additions & 4 deletions src/Enums/GenericCollectionType.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
#pragma warning disable CA2211

namespace Soenneker.Utils.AutoBogus.Enums;
namespace Soenneker.Utils.AutoBogus.Enums;

public enum GenericCollectionType
{
Expand All @@ -9,7 +7,7 @@ public enum GenericCollectionType
Collection = 2,
ReadOnlyCollection = 3,
Set = 4,
ListType = 5,
List = 5,
ReadOnlyList = 6,
SortedList = 7,
ReadOnlyDictionary = 8,
Expand Down
10 changes: 7 additions & 3 deletions src/Extensions/TypeArrayExtension.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ internal static (CachedType?, GenericCollectionType?) GetTypeOfGenericCollection
}

break;
case "ReadOnlyCollection`1":
return (cachedType, GenericCollectionType.ReadOnlyCollection);
case "Collection`1":
return (cachedType, GenericCollectionType.Collection);
case "IImmutableDictionary`2":
if (genericCollectionType < GenericCollectionType.ImmutableDictionary)
{
Expand All @@ -56,10 +60,10 @@ internal static (CachedType?, GenericCollectionType?) GetTypeOfGenericCollection
}

break;
case "Ilist`1":
if (genericCollectionType < GenericCollectionType.ListType)
case "IList`1":
if (genericCollectionType < GenericCollectionType.List)
{
genericCollectionType = GenericCollectionType.ListType;
genericCollectionType = GenericCollectionType.List;
returnType = cachedType;
}

Expand Down
12 changes: 10 additions & 2 deletions src/Generators/AutoFakerGeneratorFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -114,10 +114,18 @@ internal static IAutoFakerGenerator CreateGenerator(AutoFakerContext context)

return CreateGenericGenerator(CachedTypeService.DictionaryGenerator.Value, keyType, valueType);
}
case GenericCollectionType.ReadOnlyList:
case GenericCollectionType.ListType:
case GenericCollectionType.ReadOnlyCollection:
{
CachedType elementType = generics[0];
return CreateGenericGenerator(CachedTypeService.ReadOnlyCollectionGenerator.Value, elementType);
}
case GenericCollectionType.Collection:
{
CachedType elementType = generics[0];
return CreateGenericGenerator(CachedTypeService.CollectionGenerator.Value, elementType);
}
case GenericCollectionType.ReadOnlyList:
case GenericCollectionType.List:
{
CachedType elementType = generics[0];
return CreateGenericGenerator(CachedTypeService.ListGenerator.Value, elementType);
Expand Down
42 changes: 42 additions & 0 deletions src/Generators/Types/CollectionGenerator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using Soenneker.Utils.AutoBogus.Context;
using Soenneker.Utils.AutoBogus.Extensions;
using Soenneker.Utils.AutoBogus.Generators.Abstract;

namespace Soenneker.Utils.AutoBogus.Generators.Types;

internal sealed class CollectionGenerator<TType> : IAutoFakerGenerator
{
object IAutoFakerGenerator.Generate(AutoFakerContext context)
{
Collection<TType> collection;

if (context.CachedType.IsInterface)
{
collection = [];
}
else
{
try
{
collection = context.CachedType.CreateInstance<Collection<TType>>();
}
catch (Exception ex)
{
collection = [];
}
}

List<TType> items = context.GenerateMany<TType>();

for (var i = 0; i < items.Count; i++)
{
TType item = items[i];
collection.Add(item);
}

return collection;
}
}
26 changes: 26 additions & 0 deletions src/Generators/Types/ReadOnlyCollectionGenerator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using Soenneker.Utils.AutoBogus.Context;
using Soenneker.Utils.AutoBogus.Extensions;
using Soenneker.Utils.AutoBogus.Generators.Abstract;

namespace Soenneker.Utils.AutoBogus.Generators.Types;

internal sealed class ReadOnlyCollectionGenerator<TType> : IAutoFakerGenerator
{
object IAutoFakerGenerator.Generate(AutoFakerContext context)
{
List<TType> items = context.GenerateMany<TType>();

try
{
ReadOnlyCollection<TType> collection = new ReadOnlyCollection<TType>(items);
return collection;
}
catch (Exception ex)
{
return null!;
}
}
}
28 changes: 15 additions & 13 deletions src/Services/CachedTypeService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,20 @@ internal static class CachedTypeService
{
public static ReflectionCache ReflectionCache { get; set; } = new ReflectionCache();

internal static readonly Lazy<CachedType> TypeGenerator = new(() => ReflectionCache.GetCachedType(typeof(TypeGenerator<>)));
internal static readonly Lazy<CachedType> EnumerableGenerator = new(() => ReflectionCache.GetCachedType(typeof(EnumerableGenerator<>)));
internal static readonly Lazy<CachedType> ListGenerator = new(() => ReflectionCache.GetCachedType(typeof(ListGenerator<>)));
internal static readonly Lazy<CachedType> SetGenerator = new(() => ReflectionCache.GetCachedType(typeof(SetGenerator<>)));
internal static readonly Lazy<CachedType> ReadOnlyDictionaryGenerator = new(() => ReflectionCache.GetCachedType(typeof(ReadOnlyDictionaryGenerator<,>)));
internal static readonly Lazy<CachedType> NullableGenerator = new(() => ReflectionCache.GetCachedType(typeof(NullableGenerator<>)));
internal static readonly Lazy<CachedType> EnumGenerator = new(() => ReflectionCache.GetCachedType(typeof(EnumGenerator<>)));
internal static readonly Lazy<CachedType> ArrayGenerator = new(() => ReflectionCache.GetCachedType(typeof(ArrayGenerator<>)));
internal static readonly Lazy<CachedType> DictionaryGenerator = new(() => ReflectionCache.GetCachedType(typeof(DictionaryGenerator<,>)));
internal static readonly Lazy<CachedType> TypeGenerator = new(() => ReflectionCache.GetCachedType(typeof(TypeGenerator<>)), System.Threading.LazyThreadSafetyMode.PublicationOnly);
internal static readonly Lazy<CachedType> EnumerableGenerator = new(() => ReflectionCache.GetCachedType(typeof(EnumerableGenerator<>)), System.Threading.LazyThreadSafetyMode.PublicationOnly);
internal static readonly Lazy<CachedType> ListGenerator = new(() => ReflectionCache.GetCachedType(typeof(ListGenerator<>)), System.Threading.LazyThreadSafetyMode.PublicationOnly);
internal static readonly Lazy<CachedType> SetGenerator = new(() => ReflectionCache.GetCachedType(typeof(SetGenerator<>)), System.Threading.LazyThreadSafetyMode.PublicationOnly);
internal static readonly Lazy<CachedType> ReadOnlyDictionaryGenerator = new(() => ReflectionCache.GetCachedType(typeof(ReadOnlyDictionaryGenerator<,>)), System.Threading.LazyThreadSafetyMode.PublicationOnly);
internal static readonly Lazy<CachedType> NullableGenerator = new(() => ReflectionCache.GetCachedType(typeof(NullableGenerator<>)), System.Threading.LazyThreadSafetyMode.PublicationOnly);
internal static readonly Lazy<CachedType> EnumGenerator = new(() => ReflectionCache.GetCachedType(typeof(EnumGenerator<>)), System.Threading.LazyThreadSafetyMode.PublicationOnly);
internal static readonly Lazy<CachedType> ArrayGenerator = new(() => ReflectionCache.GetCachedType(typeof(ArrayGenerator<>)), System.Threading.LazyThreadSafetyMode.PublicationOnly);
internal static readonly Lazy<CachedType> DictionaryGenerator = new(() => ReflectionCache.GetCachedType(typeof(DictionaryGenerator<,>)), System.Threading.LazyThreadSafetyMode.PublicationOnly);
internal static readonly Lazy<CachedType> CollectionGenerator = new(() => ReflectionCache.GetCachedType(typeof(CollectionGenerator<>)), System.Threading.LazyThreadSafetyMode.PublicationOnly);
internal static readonly Lazy<CachedType> ReadOnlyCollectionGenerator = new(() => ReflectionCache.GetCachedType(typeof(ReadOnlyCollectionGenerator<>)), System.Threading.LazyThreadSafetyMode.PublicationOnly);

internal static readonly Lazy<CachedType> IDictionary = new(() => ReflectionCache.GetCachedType(typeof(IDictionary<,>)));
internal static readonly Lazy<CachedType> IEnumerable = new(() => ReflectionCache.GetCachedType(typeof(IEnumerable<>)));
internal static readonly Lazy<CachedType> AutoFaker = new(() => ReflectionCache.GetCachedType(typeof(AutoFaker)));
internal static readonly Lazy<CachedType> Object = new(() => ReflectionCache.GetCachedType(typeof(object)));
internal static readonly Lazy<CachedType> IDictionary = new(() => ReflectionCache.GetCachedType(typeof(IDictionary<,>)), System.Threading.LazyThreadSafetyMode.PublicationOnly);
internal static readonly Lazy<CachedType> IEnumerable = new(() => ReflectionCache.GetCachedType(typeof(IEnumerable<>)), System.Threading.LazyThreadSafetyMode.PublicationOnly);
internal static readonly Lazy<CachedType> AutoFaker = new(() => ReflectionCache.GetCachedType(typeof(AutoFaker)), System.Threading.LazyThreadSafetyMode.PublicationOnly);
internal static readonly Lazy<CachedType> Object = new(() => ReflectionCache.GetCachedType(typeof(object)), System.Threading.LazyThreadSafetyMode.PublicationOnly);
}
20 changes: 20 additions & 0 deletions test/Soenneker.Utils.AutoBogus.Tests/AutoFakerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,26 @@ public void Generate_TestClassIReadOnlyCollection_should_generate()
classObj.Ints.Should().NotBeNullOrEmpty();
}

[Fact]
public void Generate_TestClassCollection_should_generate()
{
var faker = new AutoFaker();

var classObj = faker.Generate<TestClassCollection>();
classObj.Should().NotBeNull();
classObj.Strings.Should().NotBeNullOrEmpty();
}

[Fact]
public void Generate_TestClassReadOnlyCollection_should_generate()
{
var faker = new AutoFaker();

var classObj = faker.Generate<TestClassReadOnlyCollection>();
classObj.Should().NotBeNull();
classObj.Strings.Should().NotBeNullOrEmpty();
}

[Fact]
public void Generate_Product_should_generate()
{
Expand Down
61 changes: 51 additions & 10 deletions test/Soenneker.Utils.AutoBogus.Tests/AutoGeneratorsFixture.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Dynamic;
using System.Linq;
using System.Reflection;
Expand Down Expand Up @@ -458,21 +459,19 @@ public void GetGenerator_Should_Return_DictionaryGenerator(Type type)
}
}

public class ListGenerator
: AutoGeneratorsFixture
public class ListGenerator: AutoGeneratorsFixture
{
[Theory]
[InlineData(typeof(IList<TestEnum>))]
[InlineData(typeof(IList<TestStruct>))]
[InlineData(typeof(IList<TestClass>))]
[InlineData(typeof(IList<ITestInterface>))]
[InlineData(typeof(IList<TestAbstractClass>))]
[InlineData(typeof(ICollection<TestEnum>))]
[InlineData(typeof(ICollection<TestStruct>))]
[InlineData(typeof(ICollection<TestClass>))]
[InlineData(typeof(ICollection<ITestInterface>))]
[InlineData(typeof(ICollection<TestAbstractClass>))]
[InlineData(typeof(List<TestEnum>))]
[InlineData(typeof(List<TestClass>))]
[InlineData(typeof(List<TestStruct>))]
[InlineData(typeof(List<ITestInterface>))]
[InlineData(typeof(List<TestAbstractClass>))]
public void Generate_Should_Return_List(Type type)
{
Type[] genericTypes = type.GetGenericArguments();
Expand All @@ -484,24 +483,66 @@ public void Generate_Should_Return_List(Type type)
list.Should().NotBeNull();
}

[Theory]
[InlineData(typeof(ICollection<TestEnum>))]
[InlineData(typeof(ICollection<TestStruct>))]
[InlineData(typeof(ICollection<TestClass>))]
[InlineData(typeof(ICollection<ITestInterface>))]
[InlineData(typeof(ICollection<TestAbstractClass>))]
[InlineData(typeof(Collection<TestEnum>))]
[InlineData(typeof(Collection<TestClass>))]
[InlineData(typeof(Collection<TestStruct>))]
[InlineData(typeof(Collection<ITestInterface>))]
[InlineData(typeof(Collection<TestAbstractClass>))]
public void Generate_Should_Return_Collection(Type type)
{
Type[] genericTypes = type.GetGenericArguments();
Type itemType = genericTypes.ElementAt(0);
AutoFakerBinderService.SetBinder(new AutoFakerBinder(new AutoFakerConfig()));
IAutoFakerGenerator generator = CreateGenerator(typeof(CollectionGenerator<>), itemType);
var collection = InvokeGenerator(type, generator) as IEnumerable;

collection.Should().NotBeNull();
}

[Theory]
[InlineData(typeof(IList<TestEnum>))]
[InlineData(typeof(IList<TestStruct>))]
[InlineData(typeof(IList<TestClass>))]
[InlineData(typeof(IList<ITestInterface>))]
[InlineData(typeof(IList<TestAbstractClass>))]
[InlineData(typeof(List<TestEnum>))]
[InlineData(typeof(List<TestClass>))]
[InlineData(typeof(List<TestStruct>))]
[InlineData(typeof(List<ITestInterface>))]
[InlineData(typeof(List<TestAbstractClass>))]
public void GetGenerator_Should_Return_ListGenerator(Type type)
{
AutoFakerContext context = CreateContext(type);
Type[] genericTypes = type.GetGenericArguments();
Type itemType = genericTypes.ElementAt(0);
Type generatorType = GetGeneratorType(typeof(ListGenerator<>), itemType);

AutoFakerGeneratorFactory.GetGenerator(context).Should().BeOfType(generatorType);
}

[Theory]
[InlineData(typeof(ICollection<TestEnum>))]
[InlineData(typeof(ICollection<TestStruct>))]
[InlineData(typeof(ICollection<TestClass>))]
[InlineData(typeof(ICollection<ITestInterface>))]
[InlineData(typeof(ICollection<TestAbstractClass>))]
[InlineData(typeof(List<TestClass>))]
public void GetGenerator_Should_Return_ListGenerator(Type type)
[InlineData(typeof(Collection<TestEnum>))]
[InlineData(typeof(Collection<TestClass>))]
[InlineData(typeof(Collection<TestStruct>))]
[InlineData(typeof(Collection<ITestInterface>))]
[InlineData(typeof(Collection<TestAbstractClass>))]
public void GetGenerator_Should_Return_CollectionGenerator(Type type)
{
AutoFakerContext context = CreateContext(type);
Type[] genericTypes = type.GetGenericArguments();
Type itemType = genericTypes.ElementAt(0);
Type generatorType = GetGeneratorType(typeof(ListGenerator<>), itemType);
Type generatorType = GetGeneratorType(typeof(CollectionGenerator<>), itemType);

AutoFakerGeneratorFactory.GetGenerator(context).Should().BeOfType(generatorType);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,15 @@ public void Setup()
}

[Benchmark]
public string Bogus_string()
public int Bogus_int()
{
return _faker.Random.Word();
return _faker.Random.Int();
}

[Benchmark]
public int Bogus_int()
public string Bogus_string()
{
return _faker.Random.Int();
return _faker.Random.Word();
}

[Benchmark]
Expand Down

0 comments on commit 852f891

Please sign in to comment.