Skip to content

Commit 8f0dc5f

Browse files
committed
Settled on object builder design
1 parent a2602a6 commit 8f0dc5f

File tree

11 files changed

+51
-70
lines changed

11 files changed

+51
-70
lines changed

TestStack.Dossier.Tests/Builders/AutoConstructorCustomerBuilder.cs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
1-
using TestStack.Dossier.BuildStrategies;
1+
using TestStack.Dossier.Factories;
22
using TestStack.Dossier.Tests.Stubs.Entities;
33

44
namespace TestStack.Dossier.Tests.Builders
55
{
66
class AutoConstructorCustomerBuilder : TestDataBuilder<Customer, AutoConstructorCustomerBuilder>
77
{
8-
protected override Customer BuildObject()
9-
{
10-
return BuilderStrategy.Apply<UseConstructor>().BuildObject(this);
11-
}
8+
public AutoConstructorCustomerBuilder()
9+
: base(new ConstructorFactory()) { }
1210

1311
public AutoConstructorCustomerBuilder WithFirstName(string firstName)
1412
{

TestStack.Dossier/BuildStrategies/BuilderStrategy.cs

Lines changed: 0 additions & 32 deletions
This file was deleted.

TestStack.Dossier/Builder.cs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,16 @@
1-
using System;
2-
3-
namespace TestStack.Dossier
1+
namespace TestStack.Dossier
42
{
3+
/// <summary>
4+
/// A stand-alone class for building objects on the fly.
5+
/// </summary>
6+
/// <typeparam name="T">The type of object this class generates.</typeparam>
57
public class Builder<T> : TestDataBuilder<T, Builder<T>>
68
where T : class
79
{
10+
/// <summary>
11+
/// Initialises a new Builder.
12+
/// </summary>
13+
/// <returns>Returns a new instance of a Builder for the type of T</returns>
814
public static Builder<T> CreateNew()
915
{
1016
return new Builder<T>();

TestStack.Dossier/BuildStrategies/AllProperties.cs renamed to TestStack.Dossier/Factories/AllPropertiesFactory.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
using Ploeh.AutoFixture;
22

3-
namespace TestStack.Dossier.BuildStrategies
3+
namespace TestStack.Dossier.Factories
44
{
55
/// <summary>
66
/// Creates an instance of an object by setting all public and private properties.
77
/// </summary>
8-
public class AllProperties : IBuildStrategy
8+
public class AllPropertiesFactory : IFactory
99
{
1010
/// <inheritdoc />
1111
public TObject BuildObject<TObject, TBuilder>(TestDataBuilder<TObject, TBuilder> builder)

TestStack.Dossier/BuildStrategies/AutoFixture.cs renamed to TestStack.Dossier/Factories/AutoFixtureFactory.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
using Ploeh.AutoFixture;
22

3-
namespace TestStack.Dossier.BuildStrategies
3+
namespace TestStack.Dossier.Factories
44
{
55
/// <summary>
66
/// Creates an instance of an object with AutoFixture.
77
/// </summary>
8-
public class AutoFixture : IBuildStrategy
8+
public class AutoFixtureFactory : IFactory
99
{
1010
/// <inheritdoc />
1111
public TObject BuildObject<TObject, TBuilder>(TestDataBuilder<TObject, TBuilder> builder)

TestStack.Dossier/BuildStrategies/UseConstructor.cs renamed to TestStack.Dossier/Factories/ConstructorFactory.cs

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
using System;
22
using System.Linq;
33
using System.Linq.Expressions;
4+
using System.Reflection;
45
using Ploeh.AutoFixture;
56

6-
namespace TestStack.Dossier.BuildStrategies
7+
namespace TestStack.Dossier.Factories
78
{
89
/// <summary>
910
/// Builds the object using the constructor with the most arguments.
1011
/// </summary>
11-
public class UseConstructor : IBuildStrategy
12+
public class ConstructorFactory : IFactory
1213
{
1314
/// <inheritdoc />
1415
public TObject BuildObject<TObject, TBuilder>(TestDataBuilder<TObject, TBuilder> builder)
@@ -24,24 +25,22 @@ public TObject BuildObject<TObject, TBuilder>(TestDataBuilder<TObject, TBuilder>
2425

2526
var parameterValues = longestConstructor
2627
.GetParameters()
27-
.Select(x => CallGetWithType(x.Name, x.ParameterType, typeof(TObject), typeof(TBuilder)));
28+
.Select(x => CallGetWithType(x.Name, x.ParameterType, typeof(TObject), typeof(TBuilder), builder));
2829

2930
return (TObject)longestConstructor.Invoke(parameterValues.ToArray());
3031
}
3132

32-
private object CallGetWithType(string propertyName, Type propertyType, Type objectType, Type builderType)
33+
private static object CallGetWithType(string propertyName, Type propertyType, Type objectType, Type builderType, object builder)
3334
{
3435
// Make a Func<TObj, TPropertyType>
3536
var expressionDelegateType = typeof(Func<,>).MakeGenericType(objectType, propertyType);
36-
37-
// Make an expression parameter of type TObj
3837
var tObjParameterType = Expression.Parameter(objectType);
3938

40-
var valueStoredInBuilder = builderType
39+
var closedGenericGetMethod = builderType
4140
.GetMethods()
42-
.First(method => method.Name == "Get" && method.ContainsGenericParameters && method.GetGenericArguments().Length == 1)
43-
.MakeGenericMethod(propertyType)
44-
.Invoke(this, new object[]
41+
.First(IsGenericGetMethod())
42+
.MakeGenericMethod(propertyType);
43+
var valueStoredInBuilder = closedGenericGetMethod.Invoke(builder, new object[]
4544
{
4645
Expression.Lambda(
4746
expressionDelegateType,
@@ -52,5 +51,9 @@ private object CallGetWithType(string propertyName, Type propertyType, Type obje
5251
return valueStoredInBuilder;
5352
}
5453

54+
private static Func<MethodInfo, bool> IsGenericGetMethod()
55+
{
56+
return method => method.Name == "Get" && method.ContainsGenericParameters && method.GetGenericArguments().Length == 1;
57+
}
5558
}
5659
}

TestStack.Dossier/BuildStrategies/IBuildStrategy.cs renamed to TestStack.Dossier/Factories/IFactory.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
namespace TestStack.Dossier.BuildStrategies
1+
namespace TestStack.Dossier.Factories
22
{
33
/// <summary>
44
/// Interface for object building strategies
55
/// </summary>
6-
public interface IBuildStrategy
6+
public interface IFactory
77
{
88
/// <summary>
99
/// Takes a builder and generates an object of the specified type.

TestStack.Dossier/BuildStrategies/PublicProperties.cs renamed to TestStack.Dossier/Factories/PublicPropertiesFactory.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
using Ploeh.AutoFixture;
22

3-
namespace TestStack.Dossier.BuildStrategies
3+
namespace TestStack.Dossier.Factories
44
{
55
/// <summary>
66
/// Creates an instance of an object by setting all public properties but not private properties.
77
/// </summary>
8-
public class PublicProperties : IBuildStrategy
8+
public class PublicPropertiesFactory : IFactory
99
{
1010
/// <inheritdoc />
1111
public TObject BuildObject<TObject, TBuilder>(TestDataBuilder<TObject, TBuilder> builder)

TestStack.Dossier/IAnonymousValueSupplier.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,7 @@ public interface IAnonymousValueSupplier
2828
/// <summary>
2929
/// Return an anonymous value for the given property and fixture.
3030
/// </summary>
31-
/// <typeparam name="TObject">The type that the property is enclosed in</typeparam>
32-
/// <typeparam name="TValue">The type of the target property - the required anonymous value is of this type</typeparam>
31+
/// <param name="type">The type that the property is enclosed in</param>
3332
/// <param name="any">Anonymous value fixture</param>
3433
/// <param name="propertyName">The name of the property to return an anonymous value for</param>
3534
/// <returns>The anonymous value</returns>

TestStack.Dossier/TestDataBuilder.cs

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
using System;
22
using System.Collections.Generic;
33
using System.Linq.Expressions;
4-
using TestStack.Dossier.BuildStrategies;
4+
using TestStack.Dossier.Factories;
55
using TestStack.Dossier.Lists;
66

77
namespace TestStack.Dossier
@@ -15,6 +15,7 @@ public abstract class TestDataBuilder<TObject, TBuilder>
1515
where TObject : class
1616
where TBuilder : TestDataBuilder<TObject, TBuilder>, new()
1717
{
18+
private readonly IFactory _factory;
1819
private readonly Dictionary<string, object> _properties = new Dictionary<string, object>(StringComparer.InvariantCultureIgnoreCase);
1920
private ProxyBuilder<TObject> _proxyBuilder;
2021

@@ -27,7 +28,16 @@ public abstract class TestDataBuilder<TObject, TBuilder>
2728
/// Default Constructor.
2829
/// </summary>
2930
protected TestDataBuilder()
31+
: this(new AllPropertiesFactory())
3032
{
33+
}
34+
35+
/// <summary>
36+
/// Allow object builder factory to be passed in
37+
/// </summary>
38+
protected TestDataBuilder(IFactory factory)
39+
{
40+
_factory = factory;
3141
Any = new AnonymousValueFixture();
3242
}
3343

@@ -76,9 +86,7 @@ public static implicit operator List<TObject>(TestDataBuilder<TObject, TBuilder>
7686
/// <returns>The built object</returns>
7787
protected virtual TObject BuildObject()
7888
{
79-
var model = BuilderStrategy
80-
.Apply<AllProperties>()
81-
.BuildObject(this);
89+
var model = _factory.BuildObject(this);
8290

8391
return model;
8492
}

0 commit comments

Comments
 (0)