Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

A 'fix' for changing context on subsequent generations #262

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 36 additions & 11 deletions src/AutoFaker{T}.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using Bogus;
Expand All @@ -19,6 +20,9 @@ public class AutoFaker<TType> : Faker<TType> where TType : class
{
public AutoFakerConfig Config { get; set; }

private ConcurrentDictionary<int, AutoFakerContext> ContextLoopUp { get; } = new();


/// <summary>
/// The <see cref="AutoFakerBinder"/> instance to use for the generation request.
/// </summary>
Expand Down Expand Up @@ -63,14 +67,23 @@ public override TType Generate(string? ruleSets = null)
{
Initialize();

AutoFakerContext context = CreateContext(ruleSets);
CreateGenerateContext(ruleSets);

PrepareCreate(context);
PrepareFinish(context);
PrepareCreate();
PrepareFinish();

return base.Generate(ruleSets);
}

private void CreateGenerateContext(string? ruleSets)
{
lock (ContextLoopUp)
{
// Think 1 needs to be added since the generate method increments the context
ContextLoopUp[FakerHub.IndexFaker+1] = CreateContext(ruleSets);
}
}

/// <summary>
/// Generates a collection of instances of type <typeparamref name="TType"/>.
/// </summary>
Expand All @@ -81,10 +94,10 @@ public override List<TType> Generate(int count, string? ruleSets = null)
{
Initialize();

AutoFakerContext context = CreateContext(ruleSets);

PrepareCreate(context);
PrepareFinish(context);
CreateGenerateContext(ruleSets);
PrepareCreate();
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could do both these methods as part of class construction since the context is no longer required.

PrepareFinish();

return base.Generate(count, ruleSets);
}
Expand All @@ -96,8 +109,8 @@ public override List<TType> Generate(int count, string? ruleSets = null)
/// <param name="ruleSets">An optional list of delimited rule sets to use for the populate request.</param>
public override void Populate(TType instance, string? ruleSets = null)
{
AutoFakerContext context = CreateContext(ruleSets);
PrepareFinish(context);
CreateGenerateContext(ruleSets);
PrepareFinish();

base.Populate(instance, ruleSets);
}
Expand Down Expand Up @@ -141,14 +154,24 @@ private List<string> ParseRuleSets(string? ruleSets)
return validRuleSets;
}

private void PrepareCreate(AutoFakerContext context)
protected AutoFakerContext GetContext(Faker fakerHub)
{
if (!ContextLoopUp.TryGetValue(fakerHub.IndexFaker, out var context))
throw new Exception("I done F'd up");

return context;
}

private void PrepareCreate()
{
// Check a create handler hasn't previously been set or configured externally
if (_createInitialized || CreateActions[currentRuleSet] != null)
return;

CreateActions[currentRuleSet] = faker =>
{
var context = GetContext(faker);

// Only auto create if the 'default' rule set is defined for generation
// This is because any specific rule sets are expected to handle the full creation
if (context.RuleSets != null && context.RuleSets.Contains(currentRuleSet))
Expand Down Expand Up @@ -198,7 +221,7 @@ private void PrepareCreate(AutoFakerContext context)
_createInitialized = true;
}

private void PrepareFinish(AutoFakerContext context)
private void PrepareFinish()
{
if (_finishInitialized)
return;
Expand All @@ -207,6 +230,8 @@ private void PrepareFinish(AutoFakerContext context)

FinishWith((faker, instance) =>
{
var context = GetContext(faker);

if (instance == null)
return;

Expand Down
65 changes: 64 additions & 1 deletion test/Soenneker.Utils.AutoBogus.Tests/AutoFakerTests.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using FluentAssertions;
using System;
using FluentAssertions;
using Soenneker.Utils.AutoBogus.Tests.Dtos.Complex;
using System.Collections.Generic;
using System.Diagnostics;
Expand All @@ -10,6 +11,9 @@
using System.Reflection;
using Soenneker.Reflection.Cache.Options;
using Soenneker.Utils.AutoBogus.Config;
using Soenneker.Utils.AutoBogus.Context;
using Soenneker.Utils.AutoBogus.Generators;
using Soenneker.Utils.AutoBogus.Override;
using Soenneker.Utils.AutoBogus.Tests.Dtos;

namespace Soenneker.Utils.AutoBogus.Tests;
Expand Down Expand Up @@ -349,6 +353,65 @@ public void Generate_ImmutableArray_should_generate()
immutableListDto.Array.Should().NotBeNullOrEmpty();
}


public sealed class ExampleClass
{
public string? Value { get; init; }
public string AlwaysSet { get; init; }
}

[Fact]
public void Generate_RulesSet_Should_Generate_Wtih_Ruleset_And_Oeverride()
{
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is just or my testing, not sure how to name a test that checks for this issue I would go with

Genrate_ShouldUserCorrectRuleset_WhenRulestChangesInSubsiquentCalles

But that is a different style

var faker = new ExampleClassFaker();

var withSetNull = faker.Generate("setnull,default");

withSetNull.AlwaysSet.Should().NotBeEmpty();
withSetNull.Value.Should().BeNull();
}

[Fact]
public void Generate_RulesSet_Should_Generate_With_Custom_Faker()
{
var faker = new ExampleClassFaker();

var noRuleSets = faker.Generate();
var setNull = faker.Generate("setnull");
var setNullAndDefault = faker.Generate("setnull,default");

noRuleSets.AlwaysSet.Should().NotBeEmpty();
noRuleSets.Value.Should().NotBeEmpty();

setNull.AlwaysSet.Should().BeNull(); // <-- this is why I use both the default and set null ruleset
setNull.Value.Should().BeNull();

setNullAndDefault.AlwaysSet.Should().NotBeEmpty();
setNullAndDefault.Value.Should().BeNull();
}

public class StringOverride : AutoFakerOverride<string>
{
public override void Generate(AutoFakerOverrideContext context)
{
context.Instance = BuildStringWithPrefix(context.GenerateName);
}

public static string BuildStringWithPrefix(string prefix) =>
$"{prefix}-{Guid.NewGuid().ToString()}";
}

public sealed class ExampleClassFaker : AutoFaker<ExampleClass>
{
public ExampleClassFaker()
{
Config.Overrides ??= new List<AutoFakerGeneratorOverride>();
Config.Overrides.Add(new StringOverride());

RuleSet("setnull", set => set.RuleFor(property => property.Value, () => null));
}
}

[Fact]
public void Generate_TestClassWithAbstractClassParameter_should_generate()
{
Expand Down
2 changes: 1 addition & 1 deletion test/Soenneker.Utils.AutoBogus.Tests/Dtos/ProductCode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@
public sealed class ProductCode
{
public string SerialNumber { get; set; }
}
}
Loading