Skip to content

Commit 775f87b

Browse files
committed
Added ability to set child builders when using Builder<T>
1 parent 6f3053f commit 775f87b

File tree

8 files changed

+189
-7
lines changed

8 files changed

+189
-7
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ Prior to v2.0 this library was known as NTestDataBuilder.
6060
return Set(x => x.FirstName, firstName);
6161
}
6262
63+
// Note: we typically only start with the methods that are strictly needed so the builders are quick to write and aren't bloated'
6364
public virtual CustomerBuilder WithLastName(string lastName)
6465
{
6566
return Set(x => x.LastName, lastName);
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
using Shouldly;
2+
using TestStack.Dossier.Lists;
3+
using TestStack.Dossier.Tests.TestHelpers.Builders;
4+
using TestStack.Dossier.Tests.TestHelpers.Objects.ViewModels;
5+
using Xunit;
6+
7+
namespace TestStack.Dossier.Tests
8+
{
9+
// ReSharper disable once InconsistentNaming
10+
public class Builder_SetUsingBuilderTests
11+
{
12+
[Fact]
13+
public void GivenBuilderWithObjectPropertyNotSet_WhenBuildingTheObject_ThenThePropertyWillBeNull()
14+
{
15+
var vm = Builder<StudentViewModel>.CreateNew().Build();
16+
17+
vm.Address.ShouldBe(null);
18+
}
19+
20+
[Fact]
21+
public void GivenBuilderWithObjectPropertyNotSet_WhenBuildingAListOfObjects_ThenThePropertyWillBeNull()
22+
{
23+
var vm = Builder<StudentViewModel>.CreateListOfSize(1).BuildList()[0];
24+
25+
vm.Address.ShouldBe(null);
26+
}
27+
28+
[Fact]
29+
public void GivenBuilderWithObjectPropertySetViaBuilder_WhenBuildingTheObject_ThenThePropertyWillBeSet()
30+
{
31+
var vm = Builder<StudentViewModel>.CreateNew()
32+
.SetUsingBuilder(x => x.Address)
33+
.Build();
34+
35+
vm.Address.ShouldNotBe(null);
36+
}
37+
38+
[Fact]
39+
public void GivenBuilderWithObjectPropertySetViaBuilder_WhenBuildingAListOfObjects_ThenThePropertyWillBeSet()
40+
{
41+
var vm = Builder<StudentViewModel>.CreateListOfSize(1)
42+
.TheFirst(1)
43+
.SetUsingBuilder(x => x.Address)
44+
.BuildList()[0];
45+
46+
vm.Address.ShouldNotBe(null);
47+
}
48+
49+
[Fact]
50+
public void GivenBuilderWithObjectPropertySetViaBuilderAndCustomisation_WhenBuildingTheObject_ThenThePropertyWillBeSetIncludingTheCustomisation()
51+
{
52+
var vm = Builder<StudentViewModel>.CreateNew()
53+
.SetUsingBuilder(x => x.Address, b => b.Set(x => x.Street, "A street"))
54+
.Build();
55+
56+
vm.Address.ShouldNotBe(null);
57+
vm.Address.Street.ShouldBe("A street");
58+
}
59+
60+
[Fact]
61+
public void GivenBuilderWithObjectPropertySetViaBuilderAndCustomisation_WhenBuildingAListOfObjects_ThenThePropertyWillBeSetIncludingTheCustomisation()
62+
{
63+
var vm = Builder<StudentViewModel>.CreateListOfSize(1)
64+
.All()
65+
.SetUsingBuilder(x => x.Address, b => b.Set(x => x.Street, "A street"))
66+
.BuildList()[0];
67+
68+
vm.Address.ShouldNotBe(null);
69+
vm.Address.Street.ShouldBe("A street");
70+
}
71+
72+
[Fact]
73+
public void GivenBuilderWithObjectPropertySetViaCustomBuilder_WhenBuildingTheObject_ThenThePropertyWillBeSet()
74+
{
75+
var vm = Builder<StudentViewModel>.CreateNew()
76+
.SetUsingBuilder<AddressViewModel, AddressViewModelBuilder>(x => x.Address)
77+
.Build();
78+
79+
vm.Address.ShouldNotBe(null);
80+
}
81+
82+
[Fact]
83+
public void GivenBuilderWithObjectPropertySetViaCustomBuilder_WhenBuildingAListOfObjects_ThenThePropertyWillBeSet()
84+
{
85+
var vm = Builder<StudentViewModel>.CreateListOfSize(1)
86+
.All()
87+
.SetUsingBuilder<AddressViewModel, AddressViewModelBuilder>(x => x.Address)
88+
.BuildList()[0];
89+
90+
vm.Address.ShouldNotBe(null);
91+
}
92+
93+
[Fact]
94+
public void GivenBuilderWithObjectPropertySetViaCustomBuilderAndCustomisation_WhenBuildingTheObject_ThenThePropertyWillBeSetIncludingTheCustomisation()
95+
{
96+
var vm = Builder<StudentViewModel>.CreateNew()
97+
.SetUsingBuilder<AddressViewModel, AddressViewModelBuilder>(x => x.Address, b => b.WithStreet("A street"))
98+
.Build();
99+
100+
vm.Address.ShouldNotBe(null);
101+
vm.Address.Street.ShouldBe("A street");
102+
}
103+
104+
[Fact]
105+
public void GivenBuilderWithObjectPropertySetViaCustomBuilderAndCustomisation_WhenBuildingAListOfObjects_ThenThePropertyWillBeSetIncludingTheCustomisation()
106+
{
107+
var vm = Builder<StudentViewModel>.CreateListOfSize(1)
108+
.All()
109+
.SetUsingBuilder<AddressViewModel, AddressViewModelBuilder>(x => x.Address, b => b.WithStreet("A street"))
110+
.BuildList()[0];
111+
112+
vm.Address.ShouldNotBe(null);
113+
vm.Address.Street.ShouldBe("A street");
114+
}
115+
}
116+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
using TestStack.Dossier.Factories;
2+
using TestStack.Dossier.Tests.TestHelpers.Objects.ViewModels;
3+
4+
namespace TestStack.Dossier.Tests.TestHelpers.Builders
5+
{
6+
public class AddressViewModelBuilder : TestDataBuilder<AddressViewModel, AddressViewModelBuilder>
7+
{
8+
public virtual AddressViewModelBuilder WithStreet(string street)
9+
{
10+
return Set(x => x.Street, street);
11+
}
12+
13+
protected override AddressViewModel BuildObject()
14+
{
15+
return BuildUsing<PublicPropertySettersFactory>();
16+
}
17+
}
18+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
namespace TestStack.Dossier.Tests.TestHelpers.Objects.ViewModels
2+
{
3+
public class AddressViewModel
4+
{
5+
public string Street { get; set; }
6+
public string Suburb { get; set; }
7+
}
8+
}

TestStack.Dossier.Tests/TestHelpers/Objects/ViewModels/StudentViewModel.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,5 +31,7 @@ public string FullName
3131
public DateTime EnrollmentDate { get; set; }
3232

3333
public Grade Grade { get; set; }
34+
35+
public AddressViewModel Address { get; set; }
3436
}
3537
}

TestStack.Dossier.Tests/TestStack.Dossier.Tests.csproj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@
5757
<ItemGroup>
5858
<Compile Include="Builder_CreateListTests.cs" />
5959
<Compile Include="Builder_CreateNewTests.cs" />
60+
<Compile Include="Builder_SetUsingBuilderTests.cs" />
61+
<Compile Include="TestHelpers\Builders\AddressViewModelBuilder.cs" />
6062
<Compile Include="TestHelpers\Builders\AutoConstructorCustomerBuilder.cs" />
6163
<Compile Include="ChildBuilderTests.cs" />
6264
<Compile Include="Factories\AllPropertiesFactoryTests.cs" />
@@ -90,6 +92,7 @@
9092
<Compile Include="GetSetTests.cs" />
9193
<Compile Include="Properties\AssemblyInfo.cs" />
9294
<Compile Include="ProxyBuilderTests.cs" />
95+
<Compile Include="TestHelpers\Objects\ViewModels\AddressViewModel.cs" />
9396
<Compile Include="TestHelpers\Objects\ViewModels\Grade.cs" />
9497
<Compile Include="TestHelpers\Objects\Examples\MixedAccessibilityDto.cs" />
9598
<Compile Include="TestHelpers\Objects\ViewModels\StudentViewModel.cs" />

TestStack.Dossier/Builder.cs

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
1-
using Ploeh.AutoFixture.Kernel;
1+
using System;
2+
using System.Linq.Expressions;
3+
using Ploeh.AutoFixture.Kernel;
24
using TestStack.Dossier.Factories;
35
using TestStack.Dossier.Lists;
46

57
namespace TestStack.Dossier
68
{
79
/// <summary>
8-
/// A generic Test Data Builder implementation for building objects on the fly.
9-
/// By default
10+
/// A generic Test Data Builder implementation for building objects on the fly
11+
/// without needing to create a custom builder.
1012
/// </summary>
1113
/// <typeparam name="T">The type of object this class generates.</typeparam>
1214
public class Builder<T> : TestDataBuilder<T, Builder<T>>
@@ -63,5 +65,37 @@ protected override T BuildObject()
6365
{
6466
return Factory.BuildObject(this);
6567
}
68+
69+
/// <summary>
70+
/// Set a property value using a custom builder.
71+
/// </summary>
72+
/// <typeparam name="TPropertyType">The type of the property being set</typeparam>
73+
/// <typeparam name="TPropertyBuilder">The type of the custom builder to build the property value using</typeparam>
74+
/// <param name="property">The property to set</param>
75+
/// <param name="modifier">An optional modifier to customise the builder</param>
76+
/// <returns>The builder so that other method calls can be chained</returns>
77+
public virtual Builder<T> SetUsingBuilder<TPropertyType, TPropertyBuilder>(
78+
Expression<Func<T, TPropertyType>> property,
79+
Func<TPropertyBuilder, TPropertyBuilder> modifier = null)
80+
where TPropertyType : class
81+
where TPropertyBuilder : TestDataBuilder<TPropertyType, TPropertyBuilder>, new()
82+
{
83+
return Set(property, GetChildBuilder<TPropertyType, TPropertyBuilder>(modifier));
84+
}
85+
86+
/// <summary>
87+
/// Set a property value using a <see cref="Builder{TPropertyType}"/>.
88+
/// </summary>
89+
/// <typeparam name="TPropertyType">The type of the property being set</typeparam>
90+
/// <param name="property">The property to set</param>
91+
/// <param name="modifier">An optional modifier to customise the builder</param>
92+
/// <returns>The builder so that other method calls can be chained</returns>
93+
public virtual Builder<T> SetUsingBuilder<TPropertyType>(
94+
Expression<Func<T, TPropertyType>> property,
95+
Func<Builder<TPropertyType>, Builder<TPropertyType>> modifier = null)
96+
where TPropertyType : class
97+
{
98+
return Set(property, GetChildBuilder<TPropertyType, Builder<TPropertyType>>(modifier));
99+
}
66100
}
67101
}

TestStack.Dossier/TestDataBuilder.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -77,10 +77,10 @@ public static implicit operator List<TObject>(TestDataBuilder<TObject, TBuilder>
7777
protected abstract TObject BuildObject();
7878

7979
/// <summary>
80-
///
80+
/// Builds the object from this builder using an <see cref="IFactory"/>.
8181
/// </summary>
82-
/// <typeparam name="TFactory"></typeparam>
83-
/// <returns></returns>
82+
/// <typeparam name="TFactory">The factory to use to build the object</typeparam>
83+
/// <returns>The built object</returns>
8484
protected TObject BuildUsing<TFactory>()
8585
where TFactory : IFactory, new()
8686
{
@@ -110,7 +110,7 @@ protected virtual void AlterProxy(TObject proxy) {}
110110
/// </summary>
111111
/// <typeparam name="TValue">The type of the property</typeparam>
112112
/// <param name="property">A lambda expression specifying the property to record a value for</param>
113-
/// <param name="value">The value to record</param>
113+
/// <param name="value">The builder so that other method calls can be chained</param>
114114
public virtual TBuilder Set<TValue>(Expression<Func<TObject, TValue>> property, TValue value)
115115
{
116116
_properties[Reflector.GetPropertyNameFor(property)] = value;

0 commit comments

Comments
 (0)