Skip to content

Commit bf636b8

Browse files
author
Pi Lanningham
committed
Adds a Set overload which takes a lambda factory method
- The specific use case for this is that we wanted to be able to use equivalence classes. However, if you just use them naively, it would generate a single value and use it for all of the instances it created. - The lambda overload not only enables this case, but gives some power to constructing incrementing counters and the like. For example, using a Queue to generate an ordered set of names, or other such use cases.
1 parent 1c8fe7b commit bf636b8

File tree

2 files changed

+53
-11
lines changed

2 files changed

+53
-11
lines changed

TestStack.Dossier.Tests/BuildTests.cs

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using Shouldly;
1+
using System.Collections.Generic;
2+
using Shouldly;
23
using TestStack.Dossier.Tests.TestHelpers.Builders;
34
using TestStack.Dossier.Tests.TestHelpers.Objects.Entities;
45
using Xunit;
@@ -47,6 +48,32 @@ public void GivenBuilder_WhenCallingSetExplicitly_ShouldOverrideValues()
4748
customer.YearJoined.ShouldBe(2014);
4849
}
4950

51+
[Fact]
52+
public void GivenBuilder_WhenCallingSetWithLambda_ShouldInvokeEachTime()
53+
{
54+
int counter = 2014;
55+
var builder = new CustomerBuilder()
56+
.Set(x => x.FirstName, "Pi")
57+
.Set(x => x.LastName, "Lanningham")
58+
.Set(x => x.YearJoined, () => counter++);
59+
60+
var customerA = builder.Build();
61+
var customerB = builder.Build();
62+
63+
customerA.YearJoined.ShouldBe(2014);
64+
customerB.YearJoined.ShouldBe(2015);
65+
66+
List<Customer> customerList = CustomerBuilder.CreateListOfSize(10)
67+
.All()
68+
.Set(x => x.YearJoined, () => counter++);
69+
int newCounter = 2016;
70+
foreach (var c in customerList)
71+
{
72+
c.YearJoined.ShouldBe(newCounter++);
73+
}
74+
75+
}
76+
5077
[Fact]
5178
public void GivenBasicBuilder_WhenCallingBuildImplicitly_ThenReturnAnObject()
5279
{

TestStack.Dossier/TestDataBuilder.cs

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ public abstract class TestDataBuilder<TObject, TBuilder>
1616
where TBuilder : TestDataBuilder<TObject, TBuilder>, new()
1717
{
1818
private readonly Dictionary<string, object> _properties = new Dictionary<string, object>(StringComparer.InvariantCultureIgnoreCase);
19+
private readonly Dictionary<string, Func<object>> _propFactories = new Dictionary<string, Func<object>>();
1920
private ProxyBuilder<TObject> _proxyBuilder;
2021

2122
/// <summary>
@@ -107,19 +108,32 @@ public TBuilder AsProxy()
107108
/// </summary>
108109
/// <param name="proxy">The proxy object</param>
109110
protected virtual void AlterProxy(TObject proxy) {}
110-
111+
111112
/// <summary>
112113
/// Records the given value for the given property from {TObject} and returns the builder to allow chaining.
113114
/// </summary>
114115
/// <typeparam name="TValue">The type of the property</typeparam>
115116
/// <param name="property">A lambda expression specifying the property to record a value for</param>
116-
/// <param name="value">The builder so that other method calls can be chained</param>
117+
/// <param name="value">The value to set the property to</param>
118+
/// <returns>The builder so that other method calls can be chained</returns>
117119
public virtual TBuilder Set<TValue>(Expression<Func<TObject, TValue>> property, TValue value)
118120
{
119121
_properties[Reflector.GetPropertyNameFor(property)] = value;
120122
return this as TBuilder;
121123
}
122124

125+
/// <summary>
126+
/// Records a given value provider for the given property from {TObject} and returns the builder to allow chaining.
127+
/// </summary>
128+
/// <typeparam name="TValue">The type of the property</typeparam>
129+
/// <param name="property">A lambda expression specifying the property to record a value for</param>
130+
/// <param name="factory">A method which produces instances of {TValue} for the property.</param>
131+
public virtual TBuilder Set<TValue>(Expression<Func<TObject, TValue>> property, Func<TValue> factory)
132+
{
133+
_propFactories[Reflector.GetPropertyNameFor(property)] = () => factory() as object;
134+
return this as TBuilder;
135+
}
136+
123137
/// <summary>
124138
/// Gets the recorded value for the given property from {TObject} or an anonymous
125139
/// value if there isn't one specified.
@@ -129,10 +143,7 @@ public virtual TBuilder Set<TValue>(Expression<Func<TObject, TValue>> property,
129143
/// <returns>The recorded value of the property or an anonymous value for it</returns>
130144
public TValue Get<TValue>(Expression<Func<TObject, TValue>> property)
131145
{
132-
if (!Has(property))
133-
return Any.Get(property);
134-
135-
return (TValue)_properties[Reflector.GetPropertyNameFor(property)];
146+
return (TValue)Get(typeof (TValue), Reflector.GetPropertyNameFor(property));
136147
}
137148

138149
/// <summary>
@@ -144,9 +155,13 @@ public TValue Get<TValue>(Expression<Func<TObject, TValue>> property)
144155
/// <returns></returns>
145156
public object Get(Type type, string propertyName)
146157
{
147-
if (!Has(propertyName))
148-
return Any.Get(type, propertyName);
149-
return _properties[propertyName];
158+
object value;
159+
if (_properties.TryGetValue(propertyName, out value)) return value;
160+
161+
Func<object> factory;
162+
if (_propFactories.TryGetValue(propertyName, out factory)) return factory();
163+
164+
return Any.Get(type, propertyName);
150165
}
151166

152167
/// <summary>
@@ -193,7 +208,7 @@ protected bool Has<TValue>(Expression<Func<TObject, TValue>> property)
193208
/// <returns>Whether or not there is a recorded value for the property</returns>
194209
protected bool Has(string propertyName)
195210
{
196-
return _properties.ContainsKey(propertyName);
211+
return _properties.ContainsKey(propertyName) || _propFactories.ContainsKey(propertyName);
197212
}
198213

199214
/// <summary>

0 commit comments

Comments
 (0)