diff --git a/README.md b/README.md index 04b2f51..8086751 100644 --- a/README.md +++ b/README.md @@ -77,7 +77,8 @@ Additionally, we can create multiple sets of objects: var hundredPeople = MagicFactory.For() .Many(10).With(x => x.Age = 5) .Plus(20).With(x => x.Age = 60) - .Build(); // creates 10 persons with age 5, and 20 with age 60 + .PlusOne().With(x => x.Age = 100) + .Build(); // creates 10 persons with age 5, 20 with age 60, and 1 with age 100 ``` ### Custom Factories @@ -172,6 +173,7 @@ dotnet stryker ## Roadmap - Support multi-level object creation +- Support custom constructor scoped by builder (for now, custom constructors are shared along the linked builders) - The default factory could have an option to fill all properties with valid values - Allow configuration by rules - Allow sequences in numbers properties and things like email, etc diff --git a/src/ForeverFactory/Builders/Adapters/MagicFactoryOneBuilderToLinkedOneBuilderAdapter.cs b/src/ForeverFactory/Builders/Adapters/MagicFactoryOneBuilderToLinkedOneBuilderAdapter.cs new file mode 100644 index 0000000..a54afb5 --- /dev/null +++ b/src/ForeverFactory/Builders/Adapters/MagicFactoryOneBuilderToLinkedOneBuilderAdapter.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; +using ForeverFactory.Builders.Common; +using ForeverFactory.Transforms; + +namespace ForeverFactory.Builders.Adapters +{ + internal class MagicFactoryOneBuilderToLinkedOneBuilderAdapter : ILinkedBuilder + where T : class + { + private readonly IOneBuilder _builder; + private readonly TransformList _defaultTransforms; + private readonly Func _customConstructor; + + public MagicFactoryOneBuilderToLinkedOneBuilderAdapter( + MagicFactory builder, + TransformList defaultTransforms, + Func customConstructor) + { + _builder = builder; + _defaultTransforms = defaultTransforms; + _customConstructor = customConstructor; + } + + public ILinkedOneBuilder PlusOne() + { + return new LinkedOneBuilder( + new SharedContext(_defaultTransforms, _customConstructor), + previous: this + ); + } + + public IManyBuilder Plus(int count) + { + return new LinkedManyBuilder(count, + new SharedContext(_defaultTransforms, _customConstructor), + previous: null + ); + } + + public IEnumerable Build() + { + yield return _builder.Build(); + } + } +} \ No newline at end of file diff --git a/src/ForeverFactory/Builders/Adapters/OneBuilderToLinkedOneBuilderAdapter.cs b/src/ForeverFactory/Builders/Adapters/OneBuilderToLinkedOneBuilderAdapter.cs new file mode 100644 index 0000000..ffcd1f6 --- /dev/null +++ b/src/ForeverFactory/Builders/Adapters/OneBuilderToLinkedOneBuilderAdapter.cs @@ -0,0 +1,33 @@ +using System.Collections.Generic; + +namespace ForeverFactory.Builders.Adapters +{ + internal class OneBuilderToLinkedOneBuilderAdapter : ILinkedBuilder + where T : class + { + private readonly OneBuilder _builder; + + public OneBuilderToLinkedOneBuilderAdapter(OneBuilder builder) + { + _builder = builder; + } + + public ILinkedOneBuilder PlusOne() + { + return new LinkedOneBuilder(_builder.SharedContext, this); + } + + public IManyBuilder Plus(int count) + { + return new LinkedManyBuilder(count, + sharedContext: _builder.SharedContext, + previous: null + ); + } + + public IEnumerable Build() + { + yield return _builder.Build(); + } + } +} \ No newline at end of file diff --git a/src/ForeverFactory/Builders/Common/BaseBuilder.cs b/src/ForeverFactory/Builders/Common/BaseBuilder.cs new file mode 100644 index 0000000..d2105dc --- /dev/null +++ b/src/ForeverFactory/Builders/Common/BaseBuilder.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; +using ForeverFactory.Transforms; + +namespace ForeverFactory.Builders.Common +{ + internal abstract class BaseBuilder + where T : class + { + public ISharedContext SharedContext { get; } + private TransformList Transforms { get; } + + protected BaseBuilder(ISharedContext sharedContext) + { + SharedContext = sharedContext; + Transforms = new TransformList(); + } + + protected void AddTransform(Transform transform) + { + Transforms.Add(transform); + } + + protected IEnumerable> GetTransformsToApply() + { + return SharedContext.DefaultTransforms + .Union(Transforms) + .AsEnumerable(); + } + + protected T CreateInstance() + { + return SharedContext.CustomConstructor?.Invoke() ?? Activator.CreateInstance(); + } + } +} \ No newline at end of file diff --git a/src/ForeverFactory/Builders/Common/ISharedContext.cs b/src/ForeverFactory/Builders/Common/ISharedContext.cs new file mode 100644 index 0000000..d739a4e --- /dev/null +++ b/src/ForeverFactory/Builders/Common/ISharedContext.cs @@ -0,0 +1,12 @@ +using System; +using ForeverFactory.Transforms; + +namespace ForeverFactory.Builders.Common +{ + internal interface ISharedContext + where T : class + { + TransformList DefaultTransforms { get; } + Func CustomConstructor { get; } + } +} \ No newline at end of file diff --git a/src/ForeverFactory/Builders/Common/SharedContext.cs b/src/ForeverFactory/Builders/Common/SharedContext.cs new file mode 100644 index 0000000..e3690eb --- /dev/null +++ b/src/ForeverFactory/Builders/Common/SharedContext.cs @@ -0,0 +1,18 @@ +using System; +using ForeverFactory.Transforms; + +namespace ForeverFactory.Builders.Common +{ + internal class SharedContext : ISharedContext + where T : class + { + public TransformList DefaultTransforms { get; } + public Func CustomConstructor { get; } + + public SharedContext(TransformList defaultTransforms, Func customConstructor) + { + DefaultTransforms = defaultTransforms; + CustomConstructor = customConstructor; + } + } +} \ No newline at end of file diff --git a/src/ForeverFactory/Builders/ILinkedBuilder.cs b/src/ForeverFactory/Builders/ILinkedBuilder.cs new file mode 100644 index 0000000..2882071 --- /dev/null +++ b/src/ForeverFactory/Builders/ILinkedBuilder.cs @@ -0,0 +1,25 @@ +using System.Collections.Generic; + +namespace ForeverFactory.Builders +{ + public interface ILinkedBuilder + { + /// + /// Creates a new builder of "T". It will build a new object, in addition to the previous configurations. + /// + /// A builder of "T" + ILinkedOneBuilder PlusOne(); + + /// + /// Creates a new set of customizable objects, following the previous sets created used the "Many" or "Plus" methods. + /// + /// The number of objects to be created. + IManyBuilder Plus(int count); + + /// + /// Builds all the objects configured, including all sets created used the "Many" or "Plus" methods. + /// + /// A collection of instances of "T", with all configurations applied. + IEnumerable Build(); + } +} \ No newline at end of file diff --git a/src/ForeverFactory/Builders/ILinkedOneBuilder.cs b/src/ForeverFactory/Builders/ILinkedOneBuilder.cs new file mode 100644 index 0000000..b41e305 --- /dev/null +++ b/src/ForeverFactory/Builders/ILinkedOneBuilder.cs @@ -0,0 +1,13 @@ +using System; + +namespace ForeverFactory.Builders +{ + public interface ILinkedOneBuilder : ILinkedBuilder + { + /// + /// Defines the default value of a property. + /// + /// Sets the value of a Property. x => x.Name = "Karen"> + ILinkedOneBuilder With(Func setMember); + } +} \ No newline at end of file diff --git a/src/ForeverFactory/Builders/IManyBuilder.cs b/src/ForeverFactory/Builders/IManyBuilder.cs index d0f2b35..a9d8a37 100644 --- a/src/ForeverFactory/Builders/IManyBuilder.cs +++ b/src/ForeverFactory/Builders/IManyBuilder.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; namespace ForeverFactory.Builders { @@ -7,7 +6,7 @@ namespace ForeverFactory.Builders /// This interface allows building many customized objects of type "T". /// /// The type of objects that will be built. - public interface IManyBuilder + public interface IManyBuilder : ILinkedBuilder { /// /// Defines the default value of a property for all instances. @@ -28,17 +27,5 @@ public interface IManyBuilder /// How many instances will have this property set. /// Sets the value of a Property. x => x.Name = "Karen" IManyBuilder WithLast(int count, Func setMember); - - /// - /// Creates a new set of customizable objects, following the previous sets created used the "Many" or "Plus" methods. - /// - /// The number of objects to be created. - IManyBuilder Plus(int count); - - /// - /// Builds all the objects configured, including all sets created used the "Many" or "Plus" methods. - /// - /// A collection of instances of "T", with all configurations applied. - IEnumerable Build(); } } \ No newline at end of file diff --git a/src/ForeverFactory/Builders/LinkedBaseBuilder.cs b/src/ForeverFactory/Builders/LinkedBaseBuilder.cs new file mode 100644 index 0000000..baff022 --- /dev/null +++ b/src/ForeverFactory/Builders/LinkedBaseBuilder.cs @@ -0,0 +1,16 @@ +using ForeverFactory.Builders.Common; + +namespace ForeverFactory.Builders +{ + internal abstract class LinkedBaseBuilder : BaseBuilder + where T : class + { + protected ILinkedBuilder Previous { get; } + + protected LinkedBaseBuilder(ISharedContext sharedContext, ILinkedBuilder previous) + : base(sharedContext) + { + Previous = previous; + } + } +} \ No newline at end of file diff --git a/src/ForeverFactory/Builders/LinkedManyBuilder.cs b/src/ForeverFactory/Builders/LinkedManyBuilder.cs new file mode 100644 index 0000000..38495ee --- /dev/null +++ b/src/ForeverFactory/Builders/LinkedManyBuilder.cs @@ -0,0 +1,87 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using ForeverFactory.Builders.Common; +using ForeverFactory.ExecutionContext; +using ForeverFactory.Transforms; +using ForeverFactory.Transforms.Conditions; + +namespace ForeverFactory.Builders +{ + internal class LinkedManyBuilder : LinkedBaseBuilder, IManyBuilder + where T : class + { + private readonly int _quantityToProduce; + + public LinkedManyBuilder(int quantityToProduce, ISharedContext sharedContext, ILinkedBuilder previous = null) + : base(sharedContext, previous) + { + _quantityToProduce = quantityToProduce; + } + + public IManyBuilder With(Func setMember) + { + AddTransform(new FuncTransform(setMember, Conditions.NoConditions())); + return this; + } + + public IManyBuilder WithFirst(int count, Func setMember) + { + ValidateCount(count); + + AddTransform(new FuncTransform(setMember, Conditions.ToApplyFirst(count, GetExecutionContext()))); + return this; + } + + public IManyBuilder WithLast(int count, Func setMember) + { + ValidateCount(count); + + AddTransform(new FuncTransform(setMember, Conditions.ToApplyLast(count, GetExecutionContext()))); + return this; + } + + private void ValidateCount(int count) + { + if (count > _quantityToProduce) + { + throw new ArgumentOutOfRangeException("count", count, + $"Count should be less or equal to the set size ({_quantityToProduce})"); + } + } + + private InstanceSetExecutionContext GetExecutionContext() => new InstanceSetExecutionContext(_quantityToProduce); + + public ILinkedOneBuilder PlusOne() + { + return new LinkedOneBuilder(SharedContext, previous: this); + } + + public IManyBuilder Plus(int count) + { + return new LinkedManyBuilder(count, SharedContext, previous: this); + } + + public IEnumerable Build() + { + foreach (var linkedInstance in Previous?.Build() ?? Enumerable.Empty()) + { + yield return linkedInstance; + } + + for (var i = 0; i < _quantityToProduce; i++) + { + var instance = CreateInstance(); + foreach (var transform in GetTransformsToApply()) + { + if (transform.ConditionToApply.CanApplyFor(index: i) is false) + continue; + + transform.ApplyTo(instance); + } + + yield return instance; + } + } + } +} \ No newline at end of file diff --git a/src/ForeverFactory/Builders/LinkedOneBuilder.cs b/src/ForeverFactory/Builders/LinkedOneBuilder.cs new file mode 100644 index 0000000..f10d9f1 --- /dev/null +++ b/src/ForeverFactory/Builders/LinkedOneBuilder.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using ForeverFactory.Builders.Common; +using ForeverFactory.Transforms; +using ForeverFactory.Transforms.Conditions; + +namespace ForeverFactory.Builders +{ + internal class LinkedOneBuilder : LinkedBaseBuilder, ILinkedOneBuilder + where T : class + { + public LinkedOneBuilder(ISharedContext sharedContext, ILinkedBuilder previous) + : base(sharedContext, previous) + { + } + + public ILinkedOneBuilder With(Func setMember) + { + AddTransform(new FuncTransform(setMember, Conditions.NoConditions())); + return this; + } + + public IEnumerable Build() + { + foreach (var linkedInstance in Previous?.Build() ?? Enumerable.Empty()) + { + yield return linkedInstance; + } + + var instance = CreateInstance(); + foreach (var transform in GetTransformsToApply()) + { + transform.ApplyTo(instance); + } + + yield return instance; + } + + public ILinkedOneBuilder PlusOne() + { + return new LinkedOneBuilder(SharedContext, previous: this); + } + + public IManyBuilder Plus(int count) + { + return new LinkedManyBuilder(count, SharedContext, previous: this); + } + } +} \ No newline at end of file diff --git a/src/ForeverFactory/Builders/ManyBuilder.cs b/src/ForeverFactory/Builders/ManyBuilder.cs deleted file mode 100644 index 20c99e6..0000000 --- a/src/ForeverFactory/Builders/ManyBuilder.cs +++ /dev/null @@ -1,116 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using ForeverFactory.ExecutionContext; -using ForeverFactory.Transforms; -using ForeverFactory.Transforms.Conditions; - -namespace ForeverFactory.Builders -{ - /* - * Assumptions: - * - transformations are applied in same order they are declared: this makes the system deterministic and - * consequently more understandable - */ - internal class ManyBuilder : IManyBuilder - where T : class - { - private readonly List> _defaultTransforms; - private readonly List> _transforms = new List>(); - private readonly int _quantityToProduce; - - private readonly Func _customConstructor; - private readonly ManyBuilder _previousBuilder; - - public ManyBuilder(int quantityToProduce, IEnumerable> defaultTransforms, Func customConstructor) - { - _defaultTransforms = defaultTransforms.ToList(); - _transforms.AddRange(_defaultTransforms); - _customConstructor = customConstructor; - _quantityToProduce = quantityToProduce; - } - - private ManyBuilder(int quantityToProduce, IEnumerable> defaultTransforms, Func customConstructor, ManyBuilder previousBuilder) - : this(quantityToProduce, defaultTransforms, customConstructor) - { - _previousBuilder = previousBuilder; - } - - private InstanceSetExecutionContext GetExecutionContext() => new InstanceSetExecutionContext(_quantityToProduce); - - /// - /// Works within the active context - /// - public IManyBuilder With(Func setMember) - { - _transforms.Add(new FuncTransform(setMember, new NoConditionToApply())); - return this; - } - - /// - /// Works within the active context - /// - /// - public IManyBuilder WithFirst(int count, Func setMember) - { - ValidateCount(count); - - _transforms.Add(new FuncTransform(setMember, new ConditionToApplyFirst(count, GetExecutionContext()))); - return this; - } - - private void ValidateCount(int count) - { - if (count > _quantityToProduce) - { - throw new ArgumentOutOfRangeException("count", count, - $"Count should be less or equal to the set size ({_quantityToProduce})"); - } - } - - /// - /// Works within the active context - /// - /// - public IManyBuilder WithLast(int count, Func setMember) - { - if (count > _quantityToProduce) - { - throw new ArgumentOutOfRangeException("count", count, - $"Count should be less or equal to the set size ({_quantityToProduce})"); - } - _transforms.Add(new FuncTransform(setMember, new ConditionToApplyLast(count, GetExecutionContext()))); - return this; - } - - /// - /// Creates a new linked context - /// - public IManyBuilder Plus(int count) - { - return new ManyBuilder(count, _defaultTransforms, _customConstructor, previousBuilder: this); - } - - public IEnumerable Build() - { - foreach (var instance in _previousBuilder?.Build() ?? Enumerable.Empty()) - { - yield return instance; - } - - for (var i = 0; i < _quantityToProduce; i++) - { - var instance = _customConstructor?.Invoke() ?? Activator.CreateInstance(); - foreach (var transform in _transforms) - { - if (transform.ConditionToApply.CanApplyFor(index: i) is false) - continue; - - transform.ApplyTo(instance); - } - - yield return instance; - } - } - } -} \ No newline at end of file diff --git a/src/ForeverFactory/Builders/OneBuilder.cs b/src/ForeverFactory/Builders/OneBuilder.cs index 997c4bb..fd2950b 100644 --- a/src/ForeverFactory/Builders/OneBuilder.cs +++ b/src/ForeverFactory/Builders/OneBuilder.cs @@ -1,36 +1,51 @@ using System; -using System.Collections.Generic; +using ForeverFactory.Builders.Adapters; +using ForeverFactory.Builders.Common; using ForeverFactory.Transforms; using ForeverFactory.Transforms.Conditions; namespace ForeverFactory.Builders { - internal class OneBuilder : IOneBuilder + internal class OneBuilder : BaseBuilder, IOneBuilder where T : class { - private readonly List> _transforms = new List>(); - private Func _customConstructor; - - public void SetCustomConstructor(Func customConstructor) + public OneBuilder(ISharedContext sharedContext) : base(sharedContext) { - _customConstructor = customConstructor; } public IOneBuilder With(Func setMember) { - _transforms.Add(new FuncTransform(setMember, new NoConditionToApply())); + AddTransform(new FuncTransform(setMember, Conditions.NoConditions())); + return this; + } + + public IOneBuilder With(Transform setMember) + { + AddTransform(setMember); return this; } public T Build() { - var instance = _customConstructor?.Invoke() ?? Activator.CreateInstance(); - foreach (var transform in _transforms) + var instance = CreateInstance(); + foreach (var transform in GetTransformsToApply()) { transform.ApplyTo(instance); } return instance; } + + public ILinkedOneBuilder PlusOne() + { + return new LinkedOneBuilder(SharedContext, new OneBuilderToLinkedOneBuilderAdapter(this)); + } + + public IManyBuilder Plus(int count) + { + return new LinkedManyBuilder(count, SharedContext, + previous: new OneBuilderToLinkedOneBuilderAdapter(this)) + ; + } } } \ No newline at end of file diff --git a/src/ForeverFactory/DefaultFactory.cs b/src/ForeverFactory/DefaultFactory.cs deleted file mode 100644 index 764172c..0000000 --- a/src/ForeverFactory/DefaultFactory.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace ForeverFactory -{ - /// - /// A customizable factory of objects of type "T". - /// - /// The type of objects that this factory will build. - public sealed class DefaultFactory : MagicFactory - where T : class - { - } -} \ No newline at end of file diff --git a/src/ForeverFactory/MagicFactory.cs b/src/ForeverFactory/MagicFactory.cs index aeeef8b..3a497a9 100644 --- a/src/ForeverFactory/MagicFactory.cs +++ b/src/ForeverFactory/MagicFactory.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; using ForeverFactory.Builders; +using ForeverFactory.Builders.Adapters; +using ForeverFactory.Builders.Common; using ForeverFactory.Transforms; using ForeverFactory.Transforms.Conditions; @@ -16,7 +18,12 @@ public static class MagicFactory /// /// The factory will build instances of this type /// Factory of T - public static MagicFactory For() where T : class => new DefaultFactory(); + public static MagicFactory For() where T : class => new DynamicFactory(); + + private sealed class DynamicFactory : MagicFactory + where T : class + { + } } /// @@ -26,8 +33,8 @@ public static class MagicFactory public abstract class MagicFactory : IOneBuilder where T : class { - private readonly OneBuilder _oneBuilder = new OneBuilder(); - private readonly List> _defaultTransforms = new List>(); + private readonly TransformList _defaultTransforms = new TransformList(); + private readonly List> _transforms = new List>(); private Func _customConstructor; /// @@ -37,7 +44,6 @@ public abstract class MagicFactory : IOneBuilder protected void UseConstructor(Func customConstructor) { _customConstructor = customConstructor; - _oneBuilder.SetCustomConstructor(customConstructor); } /// @@ -56,8 +62,7 @@ public MagicFactory UsingConstructor(Func customConstructor) /// Sets the value of a Property. x => x.Name = "Karen"> protected void Set(Func setMember) { - _defaultTransforms.Add(new FuncTransform(setMember, new NoConditionToApply())); - _oneBuilder.With(setMember); + _defaultTransforms.Add(new FuncTransform(setMember, Conditions.NoConditions())); } # region OneBuilder Wrapper @@ -68,8 +73,8 @@ protected void Set(Func setMember) /// Sets the value of a Property. x => x.Name = "Karen"> public IOneBuilder With(Func setMember) { - _oneBuilder.With(setMember); - return _oneBuilder; + _transforms.Add(new FuncTransform(setMember, Conditions.NoConditions())); + return this; } /// @@ -78,10 +83,13 @@ public IOneBuilder With(Func setMember) /// A new instance of "T", with all configurations applied. public T Build() { - return _oneBuilder.Build(); + var oneBuilder = new OneBuilder(new SharedContext(_defaultTransforms, _customConstructor)); + foreach (var transform in _transforms) + { + oneBuilder.With(transform); + } + return oneBuilder.Build(); } - - #endregion /// /// Allows to build multiple objects. This method gives access to further group customization. @@ -90,7 +98,34 @@ public T Build() /// A builder for multiple objects. public IManyBuilder Many(int count) { - return new ManyBuilder(count, _defaultTransforms, _customConstructor); + return new LinkedManyBuilder(count, new SharedContext(_defaultTransforms, _customConstructor)); } + + + /// + /// Creates a new builder of "T". It will build a new object, in addition to the previous configurations. + /// + /// A builder of "T" + public ILinkedOneBuilder PlusOne() + { + return new LinkedOneBuilder( + sharedContext: new SharedContext(_defaultTransforms,_customConstructor), + previous: new MagicFactoryOneBuilderToLinkedOneBuilderAdapter(this, _defaultTransforms, _customConstructor) + ); + } + + /// + /// Creates a new set of customizable objects, following the previous sets created used the "Many" or "Plus" methods. + /// + /// The number of objects to be created. + public IManyBuilder Plus(int count) + { + return new LinkedManyBuilder(count, + sharedContext: new SharedContext(_defaultTransforms,_customConstructor ), + previous: new MagicFactoryOneBuilderToLinkedOneBuilderAdapter(this, _defaultTransforms, _customConstructor) + ); + } + + #endregion } } \ No newline at end of file diff --git a/src/ForeverFactory/Properties/AssemblyInfo.cs b/src/ForeverFactory/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..cfeb49f --- /dev/null +++ b/src/ForeverFactory/Properties/AssemblyInfo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("ForeverFactory.Tests")] \ No newline at end of file diff --git a/src/ForeverFactory/Transforms/Conditions/ConditionToApply.cs b/src/ForeverFactory/Transforms/Conditions/ConditionToApply.cs index 096ca35..793b41a 100644 --- a/src/ForeverFactory/Transforms/Conditions/ConditionToApply.cs +++ b/src/ForeverFactory/Transforms/Conditions/ConditionToApply.cs @@ -1,4 +1,4 @@ -using ForeverFactory.ExecutionContext; +using ForeverFactory.Transforms.Conditions.ExecutionContext; namespace ForeverFactory.Transforms.Conditions { diff --git a/src/ForeverFactory/Transforms/Conditions/ConditionToApplyFirst.cs b/src/ForeverFactory/Transforms/Conditions/ConditionToApplyFirst.cs index 3c0bbdd..69f5bef 100644 --- a/src/ForeverFactory/Transforms/Conditions/ConditionToApplyFirst.cs +++ b/src/ForeverFactory/Transforms/Conditions/ConditionToApplyFirst.cs @@ -1,4 +1,4 @@ -using ForeverFactory.ExecutionContext; +using ForeverFactory.Transforms.Conditions.ExecutionContext; namespace ForeverFactory.Transforms.Conditions { diff --git a/src/ForeverFactory/Transforms/Conditions/ConditionToApplyLast.cs b/src/ForeverFactory/Transforms/Conditions/ConditionToApplyLast.cs index fff828d..893bee9 100644 --- a/src/ForeverFactory/Transforms/Conditions/ConditionToApplyLast.cs +++ b/src/ForeverFactory/Transforms/Conditions/ConditionToApplyLast.cs @@ -1,4 +1,5 @@ using ForeverFactory.ExecutionContext; +using ForeverFactory.Transforms.Conditions.ExecutionContext; namespace ForeverFactory.Transforms.Conditions { diff --git a/src/ForeverFactory/Transforms/Conditions/Conditions.cs b/src/ForeverFactory/Transforms/Conditions/Conditions.cs new file mode 100644 index 0000000..9ab878b --- /dev/null +++ b/src/ForeverFactory/Transforms/Conditions/Conditions.cs @@ -0,0 +1,13 @@ +using ForeverFactory.Transforms.Conditions.ExecutionContext; + +namespace ForeverFactory.Transforms.Conditions +{ + internal static class Conditions + { + public static NoConditionToApply NoConditions() => new NoConditionToApply(); + public static ConditionToApply ToApplyFirst(int count, IExecutionContext executionContext) + => new ConditionToApplyFirst(count, executionContext); + public static ConditionToApply ToApplyLast(int count, IExecutionContext executionContext) + => new ConditionToApplyLast(count, executionContext); + } +} \ No newline at end of file diff --git a/src/ForeverFactory/ExecutionContext/IExecutionContext.cs b/src/ForeverFactory/Transforms/Conditions/ExecutionContext/IExecutionContext.cs similarity index 58% rename from src/ForeverFactory/ExecutionContext/IExecutionContext.cs rename to src/ForeverFactory/Transforms/Conditions/ExecutionContext/IExecutionContext.cs index d85905b..f0b2086 100644 --- a/src/ForeverFactory/ExecutionContext/IExecutionContext.cs +++ b/src/ForeverFactory/Transforms/Conditions/ExecutionContext/IExecutionContext.cs @@ -1,4 +1,4 @@ -namespace ForeverFactory.ExecutionContext +namespace ForeverFactory.Transforms.Conditions.ExecutionContext { internal interface IExecutionContext { diff --git a/src/ForeverFactory/ExecutionContext/InstanceSetExecutionContext.cs b/src/ForeverFactory/Transforms/Conditions/ExecutionContext/InstanceSetExecutionContext.cs similarity index 71% rename from src/ForeverFactory/ExecutionContext/InstanceSetExecutionContext.cs rename to src/ForeverFactory/Transforms/Conditions/ExecutionContext/InstanceSetExecutionContext.cs index 8b47364..aca9ab6 100644 --- a/src/ForeverFactory/ExecutionContext/InstanceSetExecutionContext.cs +++ b/src/ForeverFactory/Transforms/Conditions/ExecutionContext/InstanceSetExecutionContext.cs @@ -1,4 +1,6 @@ -namespace ForeverFactory.ExecutionContext +using ForeverFactory.Transforms.Conditions.ExecutionContext; + +namespace ForeverFactory.ExecutionContext { internal class InstanceSetExecutionContext : IExecutionContext { diff --git a/src/ForeverFactory/Transforms/TransformList.cs b/src/ForeverFactory/Transforms/TransformList.cs new file mode 100644 index 0000000..c34ff76 --- /dev/null +++ b/src/ForeverFactory/Transforms/TransformList.cs @@ -0,0 +1,32 @@ +using System.Collections.Generic; + +namespace ForeverFactory.Transforms +{ + internal class TransformList + { + private readonly List> _transforms = new List>(); + + public void AddRange(IEnumerable> transforms) + { + _transforms.AddRange(transforms); + } + + public void Add(Transform transform) + { + _transforms.Add(transform); + } + + public TransformList Union(TransformList otherList) + { + var newList = new TransformList(); + newList.AddRange(_transforms); + newList.AddRange(otherList._transforms); + return newList; + } + + public IEnumerable> AsEnumerable() + { + return _transforms; + } + } +} \ No newline at end of file diff --git a/tests/ForeverFactory.Tests/Builders/LinkedManyBuilderTests.cs b/tests/ForeverFactory.Tests/Builders/LinkedManyBuilderTests.cs new file mode 100644 index 0000000..13ad6d3 --- /dev/null +++ b/tests/ForeverFactory.Tests/Builders/LinkedManyBuilderTests.cs @@ -0,0 +1,364 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using FluentAssertions; +using FluentAssertions.Extensions; +using ForeverFactory.Builders; +using ForeverFactory.Builders.Common; +using ForeverFactory.Transforms; +using ForeverFactory.Transforms.Conditions; +using Xunit; + +namespace ForeverFactory.Tests.Builders +{ + public class LinkedManyBuilderTests + { + [Fact] + public void It_should_produce_the_correct_number_of_instances() + { + var builder = CreateBuilder(count: 10, defaultTransforms: null, customConstructor: null); + + var persons = builder.Build() + .ToList(); + + persons.Should().HaveCount(10); + } + + + [Fact] + public void It_produces_many_instances_with_overwritten_properties() + { + var builder = CreateBuilder(count: 10); + + var songs = builder + .With(x => x.Name = "Requiem II: Dies Irae") + .With(x => x.Artist = "Giuseppe Verdi") + .Build() + .ToList(); + + songs.Should().HaveCount(10); + foreach (var song in songs) + { + song.Name.Should().Be("Requiem II: Dies Irae"); + song.Artist.Should().Be("Giuseppe Verdi"); + song.PublishDate.Should().Be(default); + } + } + + [Fact] + public void Should_throw_an_argument_exception_for_first_count_bigger_than_total_size() + { + var builder = CreateBuilder(count: 10); + + Action invalidConfigurationBuild = () => builder + .WithFirst(count: 11, x => x.PublishDate = 1.January(1500)) + .Build(); + + invalidConfigurationBuild.Should() + .Throw("it is not possible to apply transformations beyond set size"); + } + + [Fact] + public void Should_not_throw_an_argument_exception_for_first_if_count_is_same_or_less_than_total_size() + { + var builder = CreateBuilder(count: 10); + + Action invalidConfigurationBuild = () => builder + .WithFirst(count: 10, x => x.PublishDate = 1.January(1500)) + .Build(); + + invalidConfigurationBuild.Should() + .NotThrow("it is not possible to apply transformations beyond set size"); + } + + [Fact] + public void Should_throw_an_argument_exception_for_last_count_bigger_than_total_size() + { + var builder = CreateBuilder(count: 10); + + Action invalidConfigurationBuild = () => builder + .WithLast(count: 11, x => x.PublishDate = 1.January(1500)) + .Build(); + + invalidConfigurationBuild.Should() + .Throw("it is not possible to apply transformations beyond set size"); + } + + [Fact] + public void Should_not_throw_an_argument_exception_for_last_if_count_is_same_or_less_than_total_size() + { + var builder = CreateBuilder(count: 10); + + Action invalidConfigurationBuild = () => builder + .WithLast(count: 10, x => x.PublishDate = 1.January(1500)) + .Build(); + + invalidConfigurationBuild.Should() + .NotThrow("it is not possible to apply transformations beyond set size"); + } + + [Fact] + public void WithFirst_applies_transformation_only_over_the_first_n_instances_produced() + { + var builder = CreateBuilder(count: 10); + + var songs = builder + .With(x => x.Name = "Dies Irae") + .With(x => x.Artist = "Giuseppe Verdi") + .WithFirst(count: 2, x => x.Name = "Requiem II: Dies Irae") + .Build() + .ToList(); + + var firstTwo = songs.Take(2); + foreach (var song in firstTwo) + { + song.Name.Should().Be("Requiem II: Dies Irae"); + song.Artist.Should().Be("Giuseppe Verdi"); + } + + var lastEight = songs.Skip(2); + foreach (var song in lastEight) + { + song.Name.Should().Be("Dies Irae"); + song.Artist.Should().Be("Giuseppe Verdi"); + } + } + + [Fact] + public void WithLast_applies_transformation_only_over_the_last_x_instances_produced() + { + var builder = CreateBuilder(count: 10); + + var songs = builder + .With(x => x.Name = "Dies Irae") + .With(x => x.Artist = "Giuseppe Verdi") + .With(x => x.PublishDate = 1.April(1500)) + .WithLast(count: 2, x => x.Name = "Requiem II: Dies Irae") + .WithLast(count: 3, x => x.PublishDate = 1.April(1505)) + .Build() + .ToList(); + + songs.Should().HaveCount(10); + + var firstSeven = songs.Take(7); + foreach (var song in firstSeven) + { + song.Name.Should().Be("Dies Irae"); + song.Artist.Should().Be("Giuseppe Verdi"); + song.PublishDate.Should().Be(1.April(1500)); + } + + var seventhSong = songs.Skip(7).First(); + seventhSong.Name.Should().Be("Dies Irae"); + seventhSong.Artist.Should().Be("Giuseppe Verdi"); + seventhSong.PublishDate.Should().Be(1.April(1505)); + + var lastTwo = songs.Skip(8); + foreach (var song in lastTwo) + { + song.Name.Should().Be("Requiem II: Dies Irae"); + song.Artist.Should().Be("Giuseppe Verdi"); + song.PublishDate.Should().Be(1.April(1505)); + } + } + + [Fact] + public void It_should_apply_use_constructor_if_set() + { + var builder = CreateBuilder(count: 5, + customConstructor: () => new Song { PublishDate = 2.November(2000) } + ); + + var songs = builder.Build(); + + songs.Should().HaveCount(5); + foreach (var song in songs) + { + song.PublishDate.Should().Be(2.November(2000)); + } + } + + [Fact] + public void It_should_apply_default_transforms_if_set() + { + var builder = CreateBuilder(count: 5, + defaultTransforms: new Transform[] + { + BuildTransform(x => x.Name = "Hallelujah"), + BuildTransform(x => x.PublishDate = 2.April(1984)), + } + ); + + var songs = builder.Build(); + + foreach (var song in songs) + { + song.Name.Should().Be("Hallelujah"); + song.PublishDate.Should().Be(2.April(1984)); + } + } + + [Fact] + public void It_should_override_default_transforms_with_transforms_set_via_the_With_method() + { + var builder = CreateBuilder(count: 5, + defaultTransforms: new Transform[] + { + BuildTransform(x => x.Name = "Hallelujah"), + } + ); + + var song = builder + .With(x => x.Name = "Dies Irae") + .Build().First(); + + song.Name.Should().Be("Dies Irae"); + } + + [Fact] + public void It_should_create_only_the_instances_of_this_builder_if_not_linked_with_more_builders() + { + var builder = CreateBuilder(count: 5, previous: null); + + var songs = builder.Build(); + + songs.Should().HaveCount(5, "it is not linked with others"); + } + + [Fact] + public void It_should_create_only_three_instances_when_linked_to_two_more_builders() + { + var linkedManyBuilderA = CreateBuilder(count: 2, previous: null); + var linkedManyBuilderB = CreateBuilder(count: 2, previous: linkedManyBuilderA); + var linkedManyBuilderC = CreateBuilder(count: 2, previous: linkedManyBuilderB); + + var songs = linkedManyBuilderC.Build(); + + songs.Should().HaveCount(6, "there are three linked builders that produce two instances each"); + } + + [Fact] + public void All_linked_builders_should_share_the_same_building_context() + { + var builder = CreateBuilder(count: 10, + customConstructor: () => new Song {PublishDate = 20.November(2020)}, + defaultTransforms: new[] + { + BuildTransform(x => x.Name = "Dies Irae") + } + ); + + var songs = builder + .Plus(10) + .Plus(10) + .Build(); + + songs.Should().HaveCount(30, "there are three linked builders that produce 10 instances each"); + foreach (var song in songs) + { + song.PublishDate.Should().Be(20.November(2020)); + song.Name.Should().Be("Dies Irae"); + } + } + + [Fact] + public void It_should_generate_multiple_sets_of_configurable_instances2() + { + var builder = CreateBuilder(count: 10, + customConstructor: () => new Song + { + Artist = "Daft Punk", + Name = "Da Funk", + PublishDate = 1.January(1995) + } + ); + + var songs = builder + .WithLast(count: 2, x => x.PublishDate = 1.January(1999)) + .WithLast(count: 2, x => x.Name = "Around the World") + .Plus(count: 5) + .WithFirst(count: 3, x => x.PublishDate = 1.January(2000)) + .With(x => x.Name = "Get Lucky") + .Build() + .ToList(); + + songs.Should().HaveCount(15); + + var firstEight = songs.Take(8); + foreach (var song in firstEight) + { + song.Artist.Should().Be("Daft Punk"); + song.Name.Should().Be("Da Funk"); + song.PublishDate.Should().Be(1.January(1995)); + } + + var nextTwo = songs.Skip(8).Take(2); + foreach (var song in nextTwo) + { + song.Artist.Should().Be("Daft Punk"); + song.Name.Should().Be("Around the World"); + song.PublishDate.Should().Be(1.January(1999)); + } + + var nextThree = songs.Skip(10).Take(3); + foreach (var song in nextThree) + { + song.Artist.Should().Be("Daft Punk"); + song.Name.Should().Be("Get Lucky"); + song.PublishDate.Should().Be(1.January(2000)); + } + + nextTwo = songs.TakeLast(2); + foreach (var song in nextTwo) + { + song.Artist.Should().Be("Daft Punk"); + song.Name.Should().Be("Get Lucky"); + song.PublishDate.Should().Be(1.January(1995)); + } + } + + [Fact] + public void It_should_link_to_a_LinkedManyBuilder_with_method_Plus() + { + var builder = CreateBuilder(count: 2); + + IManyBuilder newBuilder = builder.Plus(5); + + newBuilder.Should().BeOfType>(); + } + + [Fact] + public void It_should_link_to_a_LinkedOneBuilder_with_method_Plus() + { + var builder = CreateBuilder(count: 2); + + ILinkedOneBuilder newBuilder = builder.PlusOne(); + + newBuilder.Should().BeOfType>(); + } + + private static LinkedManyBuilder CreateBuilder( + int count, + IEnumerable> defaultTransforms = null, + Func customConstructor = null, + ILinkedBuilder previous = null) + where T : class + { + var transformList = new TransformList(); + transformList.AddRange(defaultTransforms ?? Enumerable.Empty>()); + + var sharedContext = new SharedContext(transformList, customConstructor); + return new LinkedManyBuilder(count, sharedContext, previous); + } + + private FuncTransform BuildTransform(Func setMember) => + new FuncTransform(setMember, Conditions.NoConditions()); + + private class Song + { + public string Name { get; set; } + public string Artist { get; set; } + public DateTime PublishDate { get; set; } + } + } +} \ No newline at end of file diff --git a/tests/ForeverFactory.Tests/Builders/LinkedOneBuilderTests.cs b/tests/ForeverFactory.Tests/Builders/LinkedOneBuilderTests.cs new file mode 100644 index 0000000..936e7fc --- /dev/null +++ b/tests/ForeverFactory.Tests/Builders/LinkedOneBuilderTests.cs @@ -0,0 +1,189 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using FluentAssertions; +using FluentAssertions.Extensions; +using ForeverFactory.Builders; +using ForeverFactory.Builders.Common; +using ForeverFactory.Transforms; +using ForeverFactory.Transforms.Conditions; +using Xunit; + +namespace ForeverFactory.Tests.Builders +{ + public class LinkedBuilderTests + { + [Fact] + public void It_should_apply_all_transforms_configured_through_With_method() + { + var linkedOneBuilder = CreateBuilder(defaultTransforms: null, customConstructor: null); + + var travelLog = linkedOneBuilder + .With(x => x.Destination = "Maui") + .With(x => x.PictureUrl = "https://somedomain/user/pictures/1234") + .With(x => x.StartDate = 10.May(2020)) + .With(x => x.EndDate = 20.May(2020)) + .Build() + .First(); + + travelLog.Destination.Should().Be("Maui"); + travelLog.PictureUrl.Should().Be("https://somedomain/user/pictures/1234"); + travelLog.StartDate.Should().Be(10.May(2020)); + travelLog.EndDate.Should().Be(20.May(2020)); + } + + [Fact] + public void It_should_apply_no_transforms_if_none_is_set() + { + var linkedOneBuilder = CreateBuilder(defaultTransforms: null, customConstructor: null); + + var travelLog = linkedOneBuilder.Build().First(); + + travelLog.Destination.Should().BeNull(); + travelLog.PictureUrl.Should().BeNull(); + travelLog.StartDate.Should().Be(default); + travelLog.EndDate.Should().Be(default); + } + + [Fact] + public void It_should_apply_use_constructor_if_set() + { + var linkedOneBuilder = CreateBuilder( + customConstructor: () => new TravelLog { StartDate = DateTime.Today + 30.Days() } + ); + + var travelLog = linkedOneBuilder.Build().First(); + + travelLog.Destination.Should().BeNull(); + travelLog.PictureUrl.Should().BeNull(); + travelLog.StartDate.Should().Be(DateTime.Today + 30.Days()); + travelLog.EndDate.Should().Be(default); + } + + [Fact] + public void It_should_apply_default_transforms_if_set() + { + var linkedOneBuilder = CreateBuilder( + defaultTransforms: new Transform[] + { + BuildTransform(x => x.Destination = "Hawaii"), + BuildTransform(x => x.StartDate = DateTime.Today + 30.Days()), + } + ); + + var travelLog = linkedOneBuilder.Build().First(); + + travelLog.Destination.Should().Be("Hawaii"); + travelLog.PictureUrl.Should().BeNull(); + travelLog.StartDate.Should().Be(DateTime.Today + 30.Days()); + travelLog.EndDate.Should().Be(default); + } + + [Fact] + public void It_should_override_default_transforms_with_transforms_set_via_the_With_method() + { + var linkedOneBuilder = CreateBuilder( + defaultTransforms: new Transform[] + { + BuildTransform(x => x.Destination = "Hawaii"), + } + ); + + var travelLog = linkedOneBuilder + .With(x => x.Destination = "Las Vegas") + .Build().First(); + + travelLog.Destination.Should().Be("Las Vegas"); + } + + [Fact] + public void It_should_create_only_one_instance_if_not_linked_with_more_builders() + { + var linkedOneBuilder = CreateBuilder(previous: null); + + var travelLogs = linkedOneBuilder.Build(); + + travelLogs.Should().HaveCount(1, "it is not linked with others"); + } + + [Fact] + public void It_should_create_only_three_instances_when_linked_to_two_more_builders() + { + var linkedOneBuilderA = CreateBuilder(previous: null); + var linkedOneBuilderB = CreateBuilder(previous: linkedOneBuilderA); + var linkedOneBuilderC = CreateBuilder(previous: linkedOneBuilderB); + + var travelLogs = linkedOneBuilderC.Build(); + + travelLogs.Should().HaveCount(3, "there are three builders linked"); + } + + [Fact] + public void All_linked_builders_should_share_the_same_building_context() + { + var builder = CreateBuilder( + customConstructor: () => new TravelLog { StartDate = 20.November(2022)}, + defaultTransforms: new [] + { + BuildTransform(x => x.Destination = "Blumenau") + } + ); + + var travelLogs = builder + .PlusOne() + .PlusOne() + .Build(); + + travelLogs.Should().HaveCount(3, "there are three builders linked"); + foreach (var log in travelLogs) + { + log.StartDate.Should().Be(20.November(2022)); + log.Destination.Should().Be("Blumenau"); + } + } + + [Fact] + public void It_should_link_to_a_LinkedManyBuilder_with_method_Plus() + { + var builder = CreateBuilder(); + + IManyBuilder newBuilder = builder.Plus(5); + + newBuilder.Should().BeOfType>(); + } + + [Fact] + public void It_should_link_to_a_LinkedOneBuilder_with_method_Plus() + { + var builder = CreateBuilder(); + + ILinkedOneBuilder newBuilder = builder.PlusOne(); + + newBuilder.Should().BeOfType>(); + } + + private static LinkedOneBuilder CreateBuilder( + IEnumerable> defaultTransforms = null, + Func customConstructor = null, + ILinkedBuilder previous = null) + where T : class + { + var transformList = new TransformList(); + transformList.AddRange(defaultTransforms ?? Enumerable.Empty>()); + + var sharedContext = new SharedContext(transformList, customConstructor); + return new LinkedOneBuilder(sharedContext, previous); + } + + private FuncTransform BuildTransform(Func setMember) => + new FuncTransform(setMember, Conditions.NoConditions()); + + private class TravelLog + { + public string Destination { get; set; } + public string PictureUrl { get; set; } + public DateTime StartDate { get; set; } + public DateTime EndDate { get; set; } + } + } +} \ No newline at end of file diff --git a/tests/ForeverFactory.Tests/Builders/OneBuilderTests.cs b/tests/ForeverFactory.Tests/Builders/OneBuilderTests.cs new file mode 100644 index 0000000..489fb4e --- /dev/null +++ b/tests/ForeverFactory.Tests/Builders/OneBuilderTests.cs @@ -0,0 +1,131 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using FluentAssertions; +using FluentAssertions.Extensions; +using ForeverFactory.Builders; +using ForeverFactory.Builders.Common; +using ForeverFactory.Transforms; +using ForeverFactory.Transforms.Conditions; +using Xunit; + +namespace ForeverFactory.Tests.Builders +{ + public class OneBuilderTests + { + [Fact] + public void It_should_apply_all_transforms_configured_through_With_method() + { + var oneBuilder = CreateBuilder(defaultTransforms: null, customConstructor: null); + + var travelLog = oneBuilder + .With(x => x.Destination = "Maui") + .With(x => x.PictureUrl = "https://somedomain/user/pictures/1234") + .With(x => x.StartDate = 10.May(2020)) + .With(x => x.EndDate = 20.May(2020)) + .Build(); + + travelLog.Destination.Should().Be("Maui"); + travelLog.PictureUrl.Should().Be("https://somedomain/user/pictures/1234"); + travelLog.StartDate.Should().Be(10.May(2020)); + travelLog.EndDate.Should().Be(20.May(2020)); + } + + [Fact] + public void It_should_apply_all_transforms_configured_through_With_method_using_a_transform() + { + var oneBuilder = CreateBuilder(defaultTransforms: null, customConstructor: null); + + var travelLog = oneBuilder + .With(BuildTransform(x => x.Destination = "Texas")) + .Build(); + + travelLog.Destination.Should().Be("Texas"); + } + + [Fact] + public void It_should_apply_no_transforms_if_none_is_set() + { + var oneBuilder = CreateBuilder(defaultTransforms: null, customConstructor: null); + + var travelLog = oneBuilder.Build(); + + travelLog.Destination.Should().BeNull(); + travelLog.PictureUrl.Should().BeNull(); + travelLog.StartDate.Should().Be(default); + travelLog.EndDate.Should().Be(default); + } + + [Fact] + public void It_should_apply_use_constructor_if_set() + { + var oneBuilder = CreateBuilder( + customConstructor: () => new TravelLog { StartDate = DateTime.Today + 30.Days() } + ); + + var travelLog = oneBuilder.Build(); + + travelLog.Destination.Should().BeNull(); + travelLog.PictureUrl.Should().BeNull(); + travelLog.StartDate.Should().Be(DateTime.Today + 30.Days()); + travelLog.EndDate.Should().Be(default); + } + + [Fact] + public void It_should_apply_default_transforms_if_set() + { + var oneBuilder = CreateBuilder( + defaultTransforms: new Transform[] + { + BuildTransform(x => x.Destination = "Hawaii"), + BuildTransform(x => x.StartDate = DateTime.Today + 30.Days()), + } + ); + + var travelLog = oneBuilder.Build(); + + travelLog.Destination.Should().Be("Hawaii"); + travelLog.PictureUrl.Should().BeNull(); + travelLog.StartDate.Should().Be(DateTime.Today + 30.Days()); + travelLog.EndDate.Should().Be(default); + } + + [Fact] + public void It_should_override_default_transforms_with_transforms_set_via_the_With_method() + { + var oneBuilder = CreateBuilder( + defaultTransforms: new Transform[] + { + BuildTransform(x => x.Destination = "Hawaii"), + } + ); + + var travelLog = oneBuilder + .With(x => x.Destination = "Las Vegas") + .Build(); + + travelLog.Destination.Should().Be("Las Vegas"); + } + + private static OneBuilder CreateBuilder(IEnumerable> defaultTransforms = null, Func customConstructor = null) + where T : class + { + var transformList = new TransformList(); + transformList.AddRange(defaultTransforms ?? Enumerable.Empty>()); + + var sharedContext = new SharedContext(transformList, customConstructor); + return new OneBuilder(sharedContext); + } + + private FuncTransform BuildTransform(Func setMember) => + new FuncTransform(setMember, Conditions.NoConditions()); + + private class TravelLog + { + public string Destination { get; set; } + public string PictureUrl { get; set; } + public DateTime StartDate { get; set; } + public DateTime EndDate { get; set; } + } + } +} \ No newline at end of file diff --git a/tests/ForeverFactory.Tests/CustomConstructorTests.cs b/tests/ForeverFactory.Tests/CustomConstructorTests.cs deleted file mode 100644 index 2c9d05e..0000000 --- a/tests/ForeverFactory.Tests/CustomConstructorTests.cs +++ /dev/null @@ -1,34 +0,0 @@ -using ForeverFactory.Tests.ExampleFactories; -using FluentAssertions; -using Xunit; - -namespace ForeverFactory.Tests -{ - public class CustomConstructorTests - { - [Fact] - public void Custom_factories_should_produce_instances_using_custom_constructor() - { - var factoryWithCustomConstructor = new ProductFactory(); - - var product = factoryWithCustomConstructor.Build(); - - product.Name.Should().Be("Nimbus 2000"); - product.Category.Should().Be("Brooms"); - product.Description.Should().Be("You will fly"); - } - - [Fact] - public void Dynamic_factories_should_produce_instances_using_custom_constructor() - { - var product = MagicFactory.For() - .UsingConstructor(() => new Product("MAG-7", "Shotgun")) - .With(x => x.Description = "South Africa, 1995") - .Build(); - - product.Name.Should().Be("MAG-7"); - product.Category.Should().Be("Shotgun"); - product.Description.Should().Be("South Africa, 1995"); - } - } -} \ No newline at end of file diff --git a/tests/ForeverFactory.Tests/CustomFactoryTests.cs b/tests/ForeverFactory.Tests/CustomFactoryTests.cs new file mode 100644 index 0000000..8100759 --- /dev/null +++ b/tests/ForeverFactory.Tests/CustomFactoryTests.cs @@ -0,0 +1,23 @@ +using ForeverFactory.Tests.ExampleFactories; +using FluentAssertions; +using Xunit; + +namespace ForeverFactory.Tests +{ + public class CustomFactoryTests + { + [Fact] + public void Custom_factories_should_produce_instances_using_custom_constructor() + { + var factoryWithCustomConstructor = new ProductFactory(); + + var product = factoryWithCustomConstructor.Build(); + + product.Name.Should().Be("Nimbus 2000"); + product.Category.Should().Be("Brooms"); + product.Description.Should().Be("The best flight await you!"); + } + + + } +} \ No newline at end of file diff --git a/tests/ForeverFactory.Tests/ExampleFactories/ProductFactory.cs b/tests/ForeverFactory.Tests/ExampleFactories/ProductFactory.cs index 1b43a55..79e344e 100644 --- a/tests/ForeverFactory.Tests/ExampleFactories/ProductFactory.cs +++ b/tests/ForeverFactory.Tests/ExampleFactories/ProductFactory.cs @@ -5,7 +5,7 @@ public class ProductFactory : MagicFactory public ProductFactory() { UseConstructor(() => new Product("Nimbus 2000", "Brooms")); - Set(x => x.Description = "You will fly"); + Set(x => x.Description = "The best flight await you!"); } } diff --git a/tests/ForeverFactory.Tests/MagicFactoryTests.cs b/tests/ForeverFactory.Tests/MagicFactoryTests.cs index e0cc617..042ef2c 100644 --- a/tests/ForeverFactory.Tests/MagicFactoryTests.cs +++ b/tests/ForeverFactory.Tests/MagicFactoryTests.cs @@ -1,5 +1,6 @@ using ForeverFactory.Tests.ExampleFactories; using FluentAssertions; +using ForeverFactory.Builders; using Xunit; namespace ForeverFactory.Tests @@ -33,5 +34,38 @@ public void Should_allow_customization() person.LastName.Should().Be("Kent"); person.Age.Should().Be(60); } + + [Fact] + public void Should_produce_instances_using_custom_constructor() + { + var product = MagicFactory.For() + .UsingConstructor(() => new Product("MAG-7", "Shotgun")) + .With(x => x.Description = "South Africa, 1995") + .Build(); + + product.Name.Should().Be("MAG-7"); + product.Category.Should().Be("Shotgun"); + product.Description.Should().Be("South Africa, 1995"); + } + + [Fact] + public void It_should_link_to_a_LinkedManyBuilder_with_method_Plus() + { + var builder = MagicFactory.For(); + + IManyBuilder newBuilder = builder.Plus(5); + + newBuilder.Should().BeOfType>(); + } + + [Fact] + public void It_should_link_to_a_LinkedOneBuilder_with_method_Plus() + { + var builder = MagicFactory.For(); + + ILinkedOneBuilder newBuilder = builder.PlusOne(); + + newBuilder.Should().BeOfType>(); + } } } \ No newline at end of file diff --git a/tests/ForeverFactory.Tests/ManyInstanceFactoryTests.cs b/tests/ForeverFactory.Tests/ManyInstanceFactoryTests.cs deleted file mode 100644 index 39de35b..0000000 --- a/tests/ForeverFactory.Tests/ManyInstanceFactoryTests.cs +++ /dev/null @@ -1,207 +0,0 @@ -using System; -using System.Linq; -using ForeverFactory.Tests.ExampleFactories; -using FluentAssertions; -using Xunit; - -namespace ForeverFactory.Tests -{ - public class ManyInstanceFactoryTests - { - private readonly PersonFactory _factory; - - public ManyInstanceFactoryTests() - { - _factory = new PersonFactory(); - } - - [Fact] - public void It_produces_many_instances_with_default_configuration() - { - var persons = _factory - .Many(count: 10) - .Build() - .ToList(); - - persons.Should().HaveCount(10); - foreach (var person in persons) - { - person.FirstName.Should().Be("Albert"); - person.LastName.Should().Be("Einstein"); - person.Age.Should().Be(56); - } - } - - [Fact] - public void It_produces_many_instances_with_overwritten_properties() - { - var persons = _factory - .Many(count: 10) - .With(x => x.Age = 19) - .With(x => x.LastName = "Nobel") - .Build() - .ToList(); - - persons.Should().HaveCount(10); - foreach (var person in persons) - { - person.FirstName.Should().Be("Albert"); - person.LastName.Should().Be("Nobel"); - person.Age.Should().Be(19); - } - } - - [Fact] - public void Should_throw_an_argument_exception_for_first_count_bigger_than_total_size() - { - Action invalidConfigurationBuild = () => _factory - .Many(count: 10) - .WithFirst(count: 11, x => x.Age = 19) - .Build(); - - invalidConfigurationBuild.Should() - .Throw("it is not possible to apply transformations beyond set size"); - } - - [Fact] - public void Should_not_throw_an_argument_exception_for_first_if_count_is_same_or_less_than_total_size() - { - Action invalidConfigurationBuild = () => _factory - .Many(count: 10) - .WithFirst(count: 10, x => x.Age = 19) - .Build(); - - invalidConfigurationBuild.Should() - .NotThrow("it is not possible to apply transformations beyond set size"); - } - - [Fact] - public void Should_throw_an_argument_exception_for_last_count_bigger_than_total_size() - { - Action invalidConfigurationBuild = () => _factory - .Many(count: 10) - .WithLast(count: 11, x => x.Age = 19) - .Build(); - - invalidConfigurationBuild.Should() - .Throw("it is not possible to apply transformations beyond set size"); - } - - [Fact] - public void Should_not_throw_an_argument_exception_for_last_if_count_is_same_or_less_than_total_size() - { - Action invalidConfigurationBuild = () => _factory - .Many(count: 10) - .WithLast(count: 10, x => x.Age = 19) - .Build(); - - invalidConfigurationBuild.Should() - .NotThrow("it is not possible to apply transformations beyond set size"); - } - - [Fact] - public void WithFirst_applies_transformation_only_over_the_first_x_instances_produced() - { - var persons = _factory - .Many(count: 10) - .WithFirst(count: 2, x => x.Age = 19) - .WithFirst(count: 2, x => x.LastName = "Nobel") - .Build() - .ToList(); - - persons.Should().HaveCount(10); - var firstTwo = persons.Take(2); - foreach (var person in firstTwo) - { - person.FirstName.Should().Be("Albert"); - person.LastName.Should().Be("Nobel"); - person.Age.Should().Be(19); - } - - var lastEight = persons.Skip(2); - foreach (var person in lastEight) - { - person.FirstName.Should().Be("Albert"); - person.LastName.Should().Be("Einstein"); - person.Age.Should().Be(56); - } - } - - [Fact] - public void WithLast_applies_transformation_only_over_the_last_x_instances_produced() - { - var persons = _factory - .Many(count: 10) - .WithLast(count: 2, x => x.Age = 19) - .WithLast(count: 2, x => x.LastName = "Nobel") - .Build() - .ToList(); - - persons.Should().HaveCount(10); - - var firstEigth = persons.Take(8); - foreach (var person in firstEigth) - { - person.FirstName.Should().Be("Albert"); - person.LastName.Should().Be("Einstein"); - person.Age.Should().Be(56); - } - - var lastTwo = persons.Skip(8); - foreach (var person in lastTwo) - { - person.FirstName.Should().Be("Albert"); - person.LastName.Should().Be("Nobel"); - person.Age.Should().Be(19); - } - } - - [Fact] - public void It_should_generate_multiple_sets_of_configurable_instances() - { - var persons = _factory - .Many(count: 10) - .WithLast(count: 2, x => x.Age = 19) - .WithLast(count: 2, x => x.LastName = "Nobel") - .Plus(count: 5) - .WithFirst(count: 3, x => x.Age = 100) - .With(x => x.FirstName = "Anna") - .Build() - .ToList(); - - persons.Should().HaveCount(15); - - var firstEight = persons.Take(8); - foreach (var person in firstEight) - { - person.FirstName.Should().Be("Albert"); - person.LastName.Should().Be("Einstein"); - person.Age.Should().Be(56); - } - - var nextTwo = persons.Skip(8).Take(2); - foreach (var person in nextTwo) - { - person.FirstName.Should().Be("Albert"); - person.LastName.Should().Be("Nobel"); - person.Age.Should().Be(19); - } - - var nextThree = persons.Skip(10).Take(3); - foreach (var person in nextThree) - { - person.FirstName.Should().Be("Anna"); - person.LastName.Should().Be("Einstein"); - person.Age.Should().Be(100); - } - - nextTwo = persons.TakeLast(2); - foreach (var person in nextTwo) - { - person.FirstName.Should().Be("Anna"); - person.LastName.Should().Be("Einstein"); - person.Age.Should().Be(56); - } - } - } -} \ No newline at end of file diff --git a/tests/ForeverFactory.Tests/NavigationBetweenInterfacesTests.cs b/tests/ForeverFactory.Tests/NavigationBetweenInterfacesTests.cs new file mode 100644 index 0000000..b204f30 --- /dev/null +++ b/tests/ForeverFactory.Tests/NavigationBetweenInterfacesTests.cs @@ -0,0 +1,49 @@ +using System.Linq; +using ForeverFactory.Tests.ExampleFactories; +using FluentAssertions; +using Xunit; + +namespace ForeverFactory.Tests +{ + public class NavigationBetweenInterfacesTests + { + private readonly PersonFactory _factory; + + public NavigationBetweenInterfacesTests() + { + _factory = new PersonFactory(); + } + + [Fact] + public void PlusOne_should_chain_previous_ManyBuilder_to_a_new_LinkedOneBuilder() + { + var persons = _factory + .Many(count: 5).With(x => x.Age = 99) + .PlusOne().With(x => x.Age = 100) + .Plus(count: 5).With(x => x.Age = 97) + .PlusOne().With(x => x.Age = 101) + .Build() + .ToList(); + + persons.Should().HaveCount(12); + + var firstTen = persons.Take(5); + foreach (var person in firstTen) + { + person.Age.Should().Be(99); + } + + var sixth = persons[5]; + sixth.Age.Should().Be(100); + + var nextFive = persons.Skip(6).Take(5); + foreach (var person in nextFive) + { + person.Age.Should().Be(97); + } + + var last = persons.Skip(11).First(); + last.Age.Should().Be(101); + } + } +} \ No newline at end of file diff --git a/tests/ForeverFactory.Tests/OneInstanceFactoryTests.cs b/tests/ForeverFactory.Tests/OneInstanceFactoryTests.cs deleted file mode 100644 index e54b180..0000000 --- a/tests/ForeverFactory.Tests/OneInstanceFactoryTests.cs +++ /dev/null @@ -1,39 +0,0 @@ -using ForeverFactory.Tests.ExampleFactories; -using FluentAssertions; -using Xunit; - -namespace ForeverFactory.Tests -{ - public class OneInstanceFactoryTests - { - private readonly PersonFactory _factory; - - public OneInstanceFactoryTests() - { - _factory = new PersonFactory(); - } - - [Fact] - public void A_custom_factory_should_produce_objects_with_values_configured_in_its_constructor() - { - var person = _factory.Build(); - - person.FirstName.Should().Be("Albert"); - person.LastName.Should().Be("Einstein"); - person.Age.Should().Be(56); - } - - [Fact] - public void Individual_properties_can_be_overwritten() - { - var person = _factory - .With(x => x.FirstName = "Guilherme") - .With(x => x.Age = 19) - .Build(); - - person.FirstName.Should().Be("Guilherme"); - person.LastName.Should().Be("Einstein"); - person.Age.Should().Be(19); - } - } -} \ No newline at end of file