diff --git a/BREAKING_CHANGES.md b/BREAKING_CHANGES.md index 832cee6..7ac50d6 100644 --- a/BREAKING_CHANGES.md +++ b/BREAKING_CHANGES.md @@ -1,7 +1,40 @@ Breaking Changes ================ -Breaking change from NTestDataBuilder -> TestStack.Dossier 1.0 +Version 3.0 +----------- + +The signature of `IAnonymousValueSupplier` has changed from: + +```c# +public interface IAnonymousValueSupplier +{ + bool CanSupplyValue(Type type, string propertyName); + TValue GenerateAnonymousValue(AnonymousValueFixture any, string propertyName); +} +``` + +To: + +```c# +public interface IAnonymousValueSupplier +{ + bool CanSupplyValue(Type type, string propertyName); + object GenerateAnonymousValue(AnonymousValueFixture any, Type type, string propertyName); +} +``` + +Note: the `GenerateAnonymousValue` method is no longer generic. + +### Reason + +In order to implement the `BuildUsing` method that allows you to build an object by convention in one line rather than having to call the constructor yourself we needed to have a non-generic version of the method. This change actually ended up making the anonymous value suppliers slightly easier to implement (no longer any need for type casting). + +### Fix + +If you have any custom anonymous value suppliers change the signature of your `GenerateAnonymousValue` method so it's no logner generic. + +Breaking change from NTestDataBuilder -> TestStack.Dossier 2.0 -------------------------------------------------------------- Namespace has changed from NTestDataBuilder to TestStack.Dossier. @@ -14,7 +47,7 @@ The project has been renamed. Do a global find and replace of `using NTestDataBuilder` with `using TestStack.Dossier`. -Breaking change from NTestDataBuilder -> TestStack.Dossier 1.0 +Breaking change from NTestDataBuilder -> TestStack.Dossier 2.0 -------------------------------------------------------------- When you don't `Set` a default value for a property that you later `Get` in your builder it will now generate an anonymous value for that property rather than throwing an exception. @@ -29,7 +62,7 @@ The old behaviour of throwing an exception if a value hasn't been specified is n If you want to fix a static value for a property then by all means you can still use `Set` calls in your builder constructor. If you aren't happy with the default anonymous value that is generated for a property you can use the `Any` property to generate a value from a different equivalence class in combination with a `Set` call in your builder constructor. -Breaking change from NTestDataBuilder -> TestStack.Dossier 1.0 +Breaking change from NTestDataBuilder -> TestStack.Dossier 2.0 -------------------------------------------------------------- The way that lists are generated no longer uses NBuilder - the new syntax is backwards compatible with NBuilder except that the namespace you need to include is different. You can also refactor your list generation to be a lot more terse, but that is optional. Any `BuildList` extension methods you created will now need to be deleted since they are no longer needed. You also need to ensure that all of the methods you call are marked virtual so the list generation can proxy those method calls. diff --git a/NextVersion.txt b/NextVersion.txt index 50aea0e..56fea8a 100644 --- a/NextVersion.txt +++ b/NextVersion.txt @@ -1 +1 @@ -2.1.0 \ No newline at end of file +3.0.0 \ No newline at end of file diff --git a/README.md b/README.md index 4851392..70a146c 100644 --- a/README.md +++ b/README.md @@ -12,164 +12,255 @@ Prior to v2.0 this library was known as NTestDataBuilder. 1. `Install-Package TestStack.Dossier` -2. Create a builder class for one of your objects, e.g. if you have a customer: - - // Customer.cs - - public class Customer - { - protected Customer() {} - - public Customer(string firstName, string lastName, int yearJoined) - { - if (string.IsNullOrEmpty(firstName)) - throw new ArgumentNullException("firstName"); - if (string.IsNullOrEmpty(lastName)) - throw new ArgumentNullException("lastName"); - - FirstName = firstName; - LastName = lastName; - YearJoined = yearJoined; - } - - public virtual int CustomerForHowManyYears(DateTime since) - { - if (since.Year < YearJoined) - throw new ArgumentException("Date must be on year or after year that customer joined.", "since"); - return since.Year - YearJoined; - } - - public virtual string FirstName { get; private set; } - public virtual string LastName { get; private set; } - public virtual int YearJoined { get; private set; } - } - - // CustomerBuilder.cs - - public class CustomerBuilder : TestDataBuilder - { - public CustomerBuilder() - { - // Can set up defaults here - any that you don't set will have an anonymous value generated by default. - WhoJoinedIn(2013); - } - - public CustomerBuilder WithFirstName(string firstName) - { - return Set(x => x.FirstName, firstName); - } - - public CustomerBuilder WithLastName(string lastName) - { - return Set(x => x.LastName, lastName); - } - - public CustomerBuilder WhoJoinedIn(int yearJoined) - { - return Set(x => x.YearJoined, yearJoined); - } - - protected override Customer BuildObject() - { - return new Customer( - Get(x => x.FirstName), - Get(x => x.LastName), - Get(x => x.YearJoined) - ); - } - } +2. Create a builder class for one of your domain objects, e.g. if you have a customer: + // Customer.cs + + public class Customer + { + protected Customer() {} + + public Customer(string firstName, string lastName, int yearJoined) + { + if (string.IsNullOrEmpty(firstName)) + throw new ArgumentNullException("firstName"); + if (string.IsNullOrEmpty(lastName)) + throw new ArgumentNullException("lastName"); + + FirstName = firstName; + LastName = lastName; + YearJoined = yearJoined; + } + + public virtual int CustomerForHowManyYears(DateTime since) + { + if (since.Year < YearJoined) + throw new ArgumentException("Date must be on year or after year that customer joined.", "since"); + return since.Year - YearJoined; + } + + public virtual string FirstName { get; private set; } + public virtual string LastName { get; private set; } + public virtual int YearJoined { get; private set; } + } + + // CustomerBuilder.cs + + public class CustomerBuilder : TestDataBuilder + { + public CustomerBuilder() + { + // Can set up defaults here - any that you don't set or subsequently override will have an anonymous value generated by default. + WhoJoinedIn(2013); + } + + // Note: the methods are virtual - this is important if you want to build lists (as per below) + public virtual CustomerBuilder WithFirstName(string firstName) + { + return Set(x => x.FirstName, firstName); + } + + // Note: we typically only start with the methods that are strictly needed so the builders are quick to write and aren't bloated' + public virtual CustomerBuilder WithLastName(string lastName) + { + return Set(x => x.LastName, lastName); + } + + public virtual CustomerBuilder WhoJoinedIn(int yearJoined) + { + return Set(x => x.YearJoined, yearJoined); + } + + protected override Customer BuildObject() + { + return new Customer( + Get(x => x.FirstName), + Get(x => x.LastName), + Get(x => x.YearJoined) + ); + // or + return BuildUsing(); + } + } 3. Use the builder in a test, e.g. - var customer = new CustomerBuilder().WithFirstName("Robert").Build(); + var customer = new CustomerBuilder() + .WithFirstName("Robert") + .Build(); 4. Consider using the Object Mother pattern in combination with the builders, see [my blog post](http://robdmoore.id.au/blog/2013/05/26/test-data-generation-the-right-way-object-mother-test-data-builders-nsubstitute-nbuilder/) for a description of how I use this library. -How can I create a list of entities using my builders? ------------------------------------------------------- +## How can I create a list of entities using my builders? This library allows you to build a list of entities fluently and tersely. Here is an example: -```c# var customers = CustomerBuilder.CreateListOfSize(5) - .TheFirst(1).WithFirstName("First") - .TheNext(1).WithLastName("Next Last") - .TheLast(1).WithLastName("Last Last") - .ThePrevious(2).With(b => b.WithLastName("last" + (++i).ToString())) - .All().WhoJoinedIn(1999) - .BuildList(); -``` + .TheFirst(1).WithFirstName("First") + .TheNext(1).WithLastName("Next Last") + .TheLast(1).WithLastName("Last Last") + .ThePrevious(2).With(b => b.WithLastName("last" + (++i).ToString())) + .All().WhoJoinedIn(1999) + .BuildList(); This would create the following (represented as json): -```json -[ - { - "FirstName":"First", - "LastName":"LastNameff51d5e5-9ce4-4710-830e-9042cfd48a8b", - "YearJoined":1999 - }, - { - "FirstName":"FirstName7b08da9c-8c13-47f7-abe9-09b73b935e1f", - "LastName":"Next Last", - "YearJoined":1999 - }, - { - "FirstName":"FirstName836d4c54-b227-4c1b-b684-de4cd940c251", - "LastName":"last1", - "YearJoined":1999 - }, - { - "FirstName":"FirstName5f53e895-921e-4130-8ed8-610b017f3b9b", - "LastName":"last2", - "YearJoined":1999 - }, - { - "FirstName":"FirstName9cf6b05f-38aa-47c1-9fd7-e3c1009cf3e4", - "LastName":"Last Last", - "YearJoined":1999 - } -] -``` + [ + { + "FirstName":"First", + "LastName":"LastNameff51d5e5-9ce4-4710-830e-9042cfd48a8b", + "YearJoined":1999 + }, + { + "FirstName":"FirstName7b08da9c-8c13-47f7-abe9-09b73b935e1f", + "LastName":"Next Last", + "YearJoined":1999 + }, + { + "FirstName":"FirstName836d4c54-b227-4c1b-b684-de4cd940c251", + "LastName":"last1", + "YearJoined":1999 + }, + { + "FirstName":"FirstName5f53e895-921e-4130-8ed8-610b017f3b9b", + "LastName":"last2", + "YearJoined":1999 + }, + { + "FirstName":"FirstName9cf6b05f-38aa-47c1-9fd7-e3c1009cf3e4", + "LastName":"Last Last", + "YearJoined":1999 + } + ] + ### Castle Dynamic Proxy Generator Exception error If you use the list builder functionality and get the following error: -> Castle.DynamicProxy.Generators.GeneratorExceptionCan not create proxy for type because it is not accessible. Make it public, or internal and mark your assembly with [assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")] attribute, because assembly is not strong-named. +> Castle.DynamicProxy.Generators.GeneratorException: Can not create proxy for type because it is not accessible. Make it public, or internal and mark your assembly with [assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")] attribute, because assembly is not strong-named. Then you either need to: * Make your builder class public * Add the following to your `AssemblyInfo.cs` file: `[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")]` -Create Entities Implicitly --------------------------- -In the previous examples, you have seen how to create entities *explicitly*, by calling the `Build()` and `BuildList()` methods. For the ultimate in terseness, you can omit these methods, and Dossier will *implicitly* call them for you. The one caveat is that you must explicitly declare the variable type rather than using the `var` keyword. +### Non-virtual method Invalid Operation Exception + +If you use the list builder functionality and get the following error: + +> System.InvalidOperationException: Tried to build a list with a builder who has non-virtual method. Please make on type virtual. + +Then you need to mark all the public methods on your builder as virtual. This is because we are using Castle Dynamic Proxy to generate lists and it can't intercept non-virtual methods. + +## Create Entities Implicitly + +In the previous examples, you have seen how to create entities *explicitly*, by calling the `Build()` and `BuildList()` methods. For the ultimate in terseness, you can omit these methods, and Dossier will *implicitly* call them for you. The one caveat is that you must explicitly declare the variable type rather than using the `var` keyword (unless you are passing into a method with the desired type). So, to create a single entity: - Customer customer = new CustomerBuilder(); + Customer customer = new CustomerBuilder(); - Customer customer = new CustomerBuilder() + Customer customer = new CustomerBuilder() .WithFirstName("Matt") .WithLastName("Kocaj") .WhoJoinedIn(2010); Or to create a list of entities: - List entities = BasicCustomerBuilder.CreateListOfSize(5); + List entities = CustomerBuilder.CreateListOfSize(5); List data = CustomerBuilder.CreateListOfSize(3) - .All().With(b => b.WithFirstName(generator.Generate().ToString())); + .TheFirst(1).WithFirstName("John"); -Anonymous Values and Equivalence Classes ----------------------------------------- +## Create object without requiring custom builder class + +If you are building domain entities, or other important classes, having a custom builder class with intention-revealing method (e.g. WithFirstName) provides terseness (avoiding lambda expressions) and allows the builder class to start forming documentation about the usage of that object. + +Sometimes though, you just want to build a class without that ceremony. Typically, we find that this applies for view models and DTOs. + +In that instance you can use the generic `Builder` implementation as shown below: + + StudentViewModel vm = Builder.CreateNew() + .Set(x => x.FirstName, "Pi") + .Set(x => x.LastName, "Lanningham") + .Set(x => x.EnrollmentDate, new DateTime(2000, 1, 1)); + + var studentViewModels = Builder.CreateListOfSize(5) + .TheFirst(1).Set(x => x.FirstName, "First") + .TheNext(1).Set(x => x.LastName, "Next Last") + .TheLast(1).Set(x => x.LastName, "Last Last") + .ThePrevious(2).With(b => b.Set(x => x.LastName, "last" + (++i).ToString())) + .All().Set(x => x.EnrollmentDate, _enrollmentDate) + .BuildList(); + +The syntax is modelled closely against what NBuilder provides and the behaviour of the class should be very similar. + +Note, that in the first example above, it was not necessary to call the `Build` method at the end of the method chain. This is because the `vm` variable has been defined as `StudentViewModel` and the C# compiler is able to infer the type and the object is set *implicitly*. + +In the second example, the `var` keyword is used to define `studentViewModels`, and so it is necessary to *explicitly* call `BuildList` to set the variable. + +### Customising the construction of the object + +By default, the longest constructor of the class you specify will be called and then all properties (with public and private setters) will be set with values you specified (or anonymous values if none were specified). + +Sometimes you might not want this behaviour, in which case you can specify a custom construction factory (see *Build objects without calling constructor* section for explanation of factories) as shown below: + + var dto = Builder + .CreateNew(new CallConstructorFactory()) + .Build(); + + var dtos = MixedAccessibilityDto dto = Builder + .CreateListOfSize(5, new CallConstructorFactory()) + .BuildList(); + +## Build objects without calling constructor + +When you extend the `TestDataBuilder` as part of creating a custom builder you will be forced to override the abstract `BuildObject` method. You have full flexibility to call the constructor of your class directly as shown above, but you can also invoke some convention-based factories to speed up the creation of your builder (also shown above) using the `BuildUsing` method. + +The `BuildUsing` method takes an instance of `IFactory`, of which you can create your own factory implementation that takes into account your own conventions or you can use one of the built-in ones: + +* `AllPropertiesFactory` - Calls the longest constructor with builder values (or anonymous values if none set) based on case-insensitive match of constructor parameter names against property names and then calls the setter on all properties (public or private) with builder values (or anonymous values if none set) +* `PublicPropertySettersFactory` - Calls the longest constructor with builder values (or anonymous values if none set) based on case-insensitive match of constructor parameter names against property names and then calls the setter on all properties with public setters with builder values (or anonymous values if none set) +* `CallConstructorFactory` - Calls the longest constructor with builder values (or anonymous values if none set) based on case-insensitive match of constructor parameter names against property names +* `AutoFixtureFactory` - Asks AutoFixture to create an anonymous instance of the class (note: does **not** use any builder values or anonymous values from Dossier) + +## Propagating the anonymous value fixture across builders + +Within a particular instance of `AnonymousValueFixture`, which is created for every builder, any generators that return a sequence of values (e.g. unique values) will be maintained. If you want to ensure that the same anonymous value fixture is used across multiple related builders then: + +* Using `CreateListOfSize` will automatically propagate the anonymous value fixture across builders +* Call the `GetChildBuilder(Func modifier = null)` method from within your custom builder, e.g.: + + public MyCustomBuilder WithSomeValue(Func modifier = null) + { + return Set(x => x.SomeValue, GetChildBuilder(modifier)); + } +* If using `Builder` then call the `SetUsingBuilder` method, e.g.: + + // Uses Builder + Builder.CreateNew() + .SetUsingBuilder(x => x.Address) + .Build() + // Uses Builder, includes customisation + Builder.CreateNew() + .SetUsingBuilder(x => x.Address, b => b.Set(y => y.Street, "A street")) + .Build() + // Uses AddressBuilder + Builder.CreateNew() + .SetUsingBuilder(x => x.Address) + .Build() + // Uses AddressBuilder, includes customisation + Builder.CreateNew() + .SetUsingBuilder(x => x.Address, b => b.Set(y => y.Street, "A street")) + .Build() + +There is currently no way to share an anonymous value fixture across unrelated builder instances. If this is something you need please raise an issue so we can discuss your requirement. + +## Anonymous Values and Equivalence Classes todo: Coming soon! -How can I create proxy objects? -------------------------------- +## How can I create proxy objects? This library integrates with [NSubstitute](http://nsubstitute.github.io/) for generating proxy objects, this means you can call the `AsProxy` method on your builder to request that the result from calling `Build` will be an NSubstitute proxy with the public properties set to return the values you have specified via your builder, e.g. @@ -180,7 +271,6 @@ This library integrates with [NSubstitute](http://nsubstitute.github.io/) for ge If you need to alter the proxy before calling `Build` to add complex behaviours that can't be expressed by the default public properties returns values then you can override the `AlterProxy` method in your builder, e.g. -```c# class CustomerBuilder : TestDataBuilder { // ... @@ -205,12 +295,10 @@ If you need to alter the proxy before calling `Build` to add complex behaviours var customer = new CustomerBuilder().AsProxy().HasBeenMemberForYears(10); var years = customer.CustomerForHowManyYears(DateTime.Now); // 10 -``` *Remember that when using proxy objects of real classes that you need to mark properties and methods as virtual and have a protected empty constructor.* -Why does TestStack.Dossier have NSubstitute and AutoFixture as dependencies? ------------------------------------------------------------------------- +## Why does TestStack.Dossier have NSubstitute and AutoFixture as dependencies? TestStack.Dossier is an opinionated framework and as such prescribes how to build your fixture data, including how to build lists, anonymous data and mock objects. Because of this we have decided to bundle it with the best of breed libraries for this purpose: AutoFixture and NSubstitute. @@ -218,7 +306,6 @@ This allows for this library to provide a rich value-add on top of the basics of If you have a suggestion for the library that can incorporate this value-add without bundling these libraries feel free to submit a pull request. -Contributions / Questions -------------------------- +## Contributions / Questions -If you would like to contribute to this project then feel free to communicate with me via Twitter @robdmoore or alternatively submit a pull request / issue. +If you would like to contribute to this project then feel free to communicate with Rob via Twitter (@robdmoore) or alternatively submit a pull request / issue. diff --git a/TestStack.Dossier.Tests/AsProxyTests.cs b/TestStack.Dossier.Tests/AsProxyTests.cs index ddf8036..4711568 100644 --- a/TestStack.Dossier.Tests/AsProxyTests.cs +++ b/TestStack.Dossier.Tests/AsProxyTests.cs @@ -1,7 +1,7 @@ using System; using NSubstitute; using Shouldly; -using TestStack.Dossier.Tests.Builders; +using TestStack.Dossier.Tests.TestHelpers.Builders; using Xunit; namespace TestStack.Dossier.Tests diff --git a/TestStack.Dossier.Tests/BuildListTests.cs b/TestStack.Dossier.Tests/BuildListTests.cs index 2b498db..5e0e15b 100644 --- a/TestStack.Dossier.Tests/BuildListTests.cs +++ b/TestStack.Dossier.Tests/BuildListTests.cs @@ -3,8 +3,8 @@ using Shouldly; using TestStack.Dossier.DataSources.Generators; using TestStack.Dossier.Lists; -using TestStack.Dossier.Tests.Builders; -using TestStack.Dossier.Tests.Entities; +using TestStack.Dossier.Tests.TestHelpers.Builders; +using TestStack.Dossier.Tests.TestHelpers.Objects.Entities; using Xunit; namespace TestStack.Dossier.Tests @@ -70,16 +70,7 @@ public void GivenListOfBuilders_WhenCallingBuildListExplicitly_ThenAListOfUnique var entities = builders.BuildList(); - entities[0].ShouldNotBe(entities[1]); - entities[0].ShouldNotBe(entities[2]); - entities[0].ShouldNotBe(entities[3]); - entities[0].ShouldNotBe(entities[4]); - entities[1].ShouldNotBe(entities[2]); - entities[1].ShouldNotBe(entities[3]); - entities[1].ShouldNotBe(entities[4]); - entities[2].ShouldNotBe(entities[3]); - entities[2].ShouldNotBe(entities[4]); - entities[3].ShouldNotBe(entities[4]); + entities.ShouldBeUnique(); } [Fact] @@ -87,16 +78,7 @@ public void GivenListOfBuilders_WhenCallingBuildListImplicitly_ThenAListOfUnique { List entities = BasicCustomerBuilder.CreateListOfSize(5); - entities[0].ShouldNotBe(entities[1]); - entities[0].ShouldNotBe(entities[2]); - entities[0].ShouldNotBe(entities[3]); - entities[0].ShouldNotBe(entities[4]); - entities[1].ShouldNotBe(entities[2]); - entities[1].ShouldNotBe(entities[3]); - entities[1].ShouldNotBe(entities[4]); - entities[2].ShouldNotBe(entities[3]); - entities[2].ShouldNotBe(entities[4]); - entities[3].ShouldNotBe(entities[4]); + entities.ShouldBeUnique(); } [Fact] diff --git a/TestStack.Dossier.Tests/BuildTests.cs b/TestStack.Dossier.Tests/BuildTests.cs index c2fcdf6..9c6d1d4 100644 --- a/TestStack.Dossier.Tests/BuildTests.cs +++ b/TestStack.Dossier.Tests/BuildTests.cs @@ -1,6 +1,6 @@ using Shouldly; -using TestStack.Dossier.Tests.Builders; -using TestStack.Dossier.Tests.Entities; +using TestStack.Dossier.Tests.TestHelpers.Builders; +using TestStack.Dossier.Tests.TestHelpers.Objects.Entities; using Xunit; namespace TestStack.Dossier.Tests @@ -80,5 +80,19 @@ public void GivenBuilder_WhenCallingSetImplicitly_ShouldOverrideValues() customer.LastName.ShouldBe("Lanningham"); customer.YearJoined.ShouldBe(2014); } + + [Fact] + public void GivenBuilderUsingConstructorReflection_WhenCallingBuildExplicitly_ShouldOverrideValues() + { + Customer customer = new AutoConstructorCustomerBuilder() + .WithFirstName("Bruce") + .WithLastName("Wayne") + .WhoJoinedIn(2012) + .Build(); + + customer.FirstName.ShouldBe("Bruce"); + customer.LastName.ShouldBe("Wayne"); + customer.YearJoined.ShouldBe(2012); + } } } \ No newline at end of file diff --git a/TestStack.Dossier.Tests/Builder_CreateListTests.cs b/TestStack.Dossier.Tests/Builder_CreateListTests.cs new file mode 100644 index 0000000..5ad8c51 --- /dev/null +++ b/TestStack.Dossier.Tests/Builder_CreateListTests.cs @@ -0,0 +1,208 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Shouldly; +using TestStack.Dossier.DataSources.Generators; +using TestStack.Dossier.Factories; +using TestStack.Dossier.Lists; +using TestStack.Dossier.Tests.TestHelpers.Objects.Examples; +using TestStack.Dossier.Tests.TestHelpers.Objects.ViewModels; +using Xunit; + +namespace TestStack.Dossier.Tests +{ + // ReSharper disable once InconsistentNaming + public class Builder_CreateListTests + { + private readonly DateTime _enrollmentDate = new DateTime(2004, 9, 9); + + [Fact] + public void GivenANormalBuilderInstance_WhenCallingIsListBuilderProxy_ThenReturnFalse() + { + var builder = Builder.CreateNew(); + + builder.IsListBuilderProxy().ShouldBe(false); + } + + [Fact] + public void GivenAListBuilderProxyInstance_WhenCallingIsListBuilderProxy_ThenReturnTrue() + { + var builder = Builder.CreateListOfSize(1).TheFirst(1); + + builder.IsListBuilderProxy().ShouldBe(true); + } + + [Fact] + public void GivenListOfBuilders_WhenCallingBuildListExplicitly_ThenAListOfObjectsOfTheRightSizeShouldBeReturned() + { + var builders = Builder.CreateListOfSize(5); + + var objects = builders.BuildList(); + + objects.Count.ShouldBe(5); + } + + [Fact] + public void GivenListOfBuilders_WhenCallingBuildListImplicitly_ThenAListOfObjectsOfTheRightSizeShouldBeReturned() + { + List objects = Builder.CreateListOfSize(5); + + objects.Count.ShouldBe(5); + } + + [Fact] + public void GivenListOfBuilders_WhenCallingBuildListExplicitly_ThenAListOfObjectsOfTheRightTypeShouldBeReturned() + { + var builders = Builder.CreateListOfSize(5); + + var objects = builders.BuildList(); + + objects.ShouldBeAssignableTo>(); + } + + [Fact] + public void GivenListOfBuilders_WhenCallingBuildListImplicitly_ThenAListOfObjectsOfTheRightTypeShouldBeReturned() + { + List objects = Builder.CreateListOfSize(5); + + objects.ShouldBeAssignableTo>(); + } + + [Fact] + public void GivenListOfBuilders_WhenCallingBuildListExplicitly_ThenAListOfUniqueObjectsShouldBeReturned() + { + var builders = Builder.CreateListOfSize(5); + + var objects = builders.BuildList(); + + objects.ShouldBeUnique(); + } + + [Fact] + public void GivenListOfBuilders_WhenCallingBuildListImplicitly_ThenAListOfUniqueObjectsShouldBeReturned() + { + List objects = Builder.CreateListOfSize(5); + + objects.ShouldBeUnique(); + } + + [Fact] + public void GivenListOfBuildersWithCustomisation_WhenBuildingObjectsExplicitly_ThenTheCustomisationShouldTakeEffect() + { + var generator = new SequentialGenerator(0, 100); + var list = Builder.CreateListOfSize(3) + .All().With(b => b.Set(x => x.FirstName, generator.Generate().ToString())); + + var data = list.BuildList(); + + data.Select(c => c.FirstName).ToArray() + .ShouldBe(new[] { "0", "1", "2" }); + } + + [Fact] + public void GivenListOfBuildersWithCustomisation_WhenBuildingObjectsImplicitly_ThenTheCustomisationShouldTakeEffect() + { + var generator = new SequentialGenerator(0, 100); + + List data = Builder.CreateListOfSize(3) + .All().With(b => b.Set(x => x.FirstName, generator.Generate().ToString())); + + data.Select(c => c.FirstName).ToArray() + .ShouldBe(new[] { "0", "1", "2" }); + } + + [Fact] + public void GivenListOfBuildersWithComplexCustomisations_WhenBuildingObjectsExplicitly_ThenThenTheListIsBuiltAndModifiedCorrectly() + { + var i = 0; + var studentViewModels = Builder.CreateListOfSize(5) + .TheFirst(1).Set(x => x.FirstName, "First") + .TheNext(1).Set(x => x.LastName, "Next Last") + .TheLast(1).Set(x => x.LastName, "Last Last") + .ThePrevious(2).With(b => b.Set(x => x.LastName, "last" + (++i).ToString())) + .All().Set(x => x.EnrollmentDate, _enrollmentDate) + .BuildList(); + + studentViewModels.ShouldBeAssignableTo>(); + studentViewModels.Count.ShouldBe(5); + studentViewModels[0].FirstName.ShouldBe("First"); + studentViewModels[1].LastName.ShouldBe("Next Last"); + studentViewModels[2].LastName.ShouldBe("last1"); + studentViewModels[3].LastName.ShouldBe("last2"); + studentViewModels[4].LastName.ShouldBe("Last Last"); + studentViewModels.ShouldAllBe(c => c.EnrollmentDate == _enrollmentDate); + } + + [Fact] + public void GivenListOfBuildersWithComplexCustomisations_WhenBuildingObjectsImplicitly_ThenThenTheListIsBuiltAndModifiedCorrectly() + { + var i = 0; + List studentViewModels = Builder.CreateListOfSize(5) + .TheFirst(1).Set(x => x.FirstName, "First") + .TheNext(1).Set(x => x.LastName, "Next Last") + .TheLast(1).Set(x => x.LastName, "Last Last") + .ThePrevious(2).With(b => b.Set(x => x.LastName, "last" + (++i).ToString())) + .All().Set(x => x.EnrollmentDate, _enrollmentDate); + + studentViewModels.ShouldBeAssignableTo>(); + studentViewModels.Count.ShouldBe(5); + studentViewModels[0].FirstName.ShouldBe("First"); + studentViewModels[1].LastName.ShouldBe("Next Last"); + studentViewModels[2].LastName.ShouldBe("last1"); + studentViewModels[3].LastName.ShouldBe("last2"); + studentViewModels[4].LastName.ShouldBe("Last Last"); + studentViewModels.ShouldAllBe(c => c.EnrollmentDate == _enrollmentDate); + } + + [Fact] + public void WhenBuildingObjectsExplicitly_ThenTheAnonymousValueFixtureIsSharedAcrossBuilders() + { + var studentViewModels = Builder.CreateListOfSize(5).BuildList(); + + studentViewModels.Select(x => x.Grade).ShouldBeUnique(); + } + + [Fact] + public void WhenBuildingObjectsImplicitly_ThenTheAnonymousValueFixtureIsSharedAcrossBuilders() + { + List studentViewModels = Builder.CreateListOfSize(5); + + studentViewModels.Select(x => x.Grade).ShouldBeUnique(); + } + + public void WhenBuildingObjectsWithCtorAndPrivateSetters_ShouldSetPrivateSettersByDefault() + { + var dto = Builder.CreateListOfSize(1) + .TheFirst(1) + .Set(x => x.SetByCtorWithPublicSetter, "1") + .Set(x => x.SetByCtorWithPrivateSetter, "2") + .Set(x => x.NotSetByCtorWithPrivateSetter, "3") + .Set(x => x.NotSetByCtorWithPublicSetter, "4") + .BuildList() + [0]; + + dto.SetByCtorWithPublicSetter.ShouldBe("1"); + dto.SetByCtorWithPrivateSetter.ShouldBe("2"); + dto.NotSetByCtorWithPrivateSetter.ShouldBe("3"); + dto.NotSetByCtorWithPublicSetter.ShouldBe("4"); + } + + [Fact] + public void GivenBuilderListWithFactoryOverride_WhenBuildingObjects_ShouldRespectOverriddenFactory() + { + var dto = Builder.CreateListOfSize(1, new CallConstructorFactory()) + .TheFirst(1) + .Set(x => x.SetByCtorWithPublicSetter, "1") + .Set(x => x.SetByCtorWithPrivateSetter, "2") + .Set(x => x.NotSetByCtorWithPrivateSetter, "3") + .Set(x => x.NotSetByCtorWithPublicSetter, "4") + .BuildList() + [0]; + + dto.SetByCtorWithPublicSetter.ShouldBe("1"); + dto.SetByCtorWithPrivateSetter.ShouldBe("2"); + dto.NotSetByCtorWithPrivateSetter.ShouldNotBe("3"); + dto.NotSetByCtorWithPublicSetter.ShouldNotBe("4"); + } + } +} \ No newline at end of file diff --git a/TestStack.Dossier.Tests/Builder_CreateNewTests.cs b/TestStack.Dossier.Tests/Builder_CreateNewTests.cs new file mode 100644 index 0000000..3581856 --- /dev/null +++ b/TestStack.Dossier.Tests/Builder_CreateNewTests.cs @@ -0,0 +1,89 @@ +using System; +using Shouldly; +using TestStack.Dossier.Factories; +using TestStack.Dossier.Tests.TestHelpers.Objects.Examples; +using TestStack.Dossier.Tests.TestHelpers.Objects.ViewModels; +using Xunit; + +namespace TestStack.Dossier.Tests +{ + // ReSharper disable once InconsistentNaming + public class Builder_CreateNewTests + { + [Fact] + public void GivenBuilder_WhenCallingBuildExplicitly_ThenReturnAnObject() + { + var builder = Builder.CreateNew(); + + var viewModel = builder.Build(); + + viewModel.ShouldBeOfType(); + } + + [Fact] + public void GivenBuilder_WhenCallingBuildImplicitly_ThenReturnAnObject() + { + StudentViewModel viewModel = Builder.CreateNew(); + + viewModel.ShouldBeOfType(); + } + + [Fact] + public void GivenBuilderWithModifications_WhenCallingBuildExplicitly_ShouldOverrideValues() + { + var builder = Builder.CreateNew() + .Set(x => x.FirstName, "Pi") + .Set(x => x.LastName, "Lanningham") + .Set(x => x.EnrollmentDate, new DateTime(2000, 1, 1)); + + var vm = builder.Build(); + + vm.FirstName.ShouldBe("Pi"); + vm.LastName.ShouldBe("Lanningham"); + vm.EnrollmentDate.ShouldBe(new DateTime(2000, 1, 1)); + } + + [Fact] + public void GivenBuilderWithModifications_WhenCallingBuildImplicitly_ShouldOverrideValues() + { + StudentViewModel vm = Builder.CreateNew() + .Set(x => x.FirstName, "Pi") + .Set(x => x.LastName, "Lanningham") + .Set(x => x.EnrollmentDate, new DateTime(2000, 1, 1)); + + vm.FirstName.ShouldBe("Pi"); + vm.LastName.ShouldBe("Lanningham"); + vm.EnrollmentDate.ShouldBe(new DateTime(2000, 1, 1)); + } + + [Fact] + public void GivenBuilder_WhenBuildingObjectWithCtorAndPrivateSetters_ShouldSetPrivateSettersByDefault() + { + MixedAccessibilityDto dto = Builder.CreateNew() + .Set(x => x.SetByCtorWithPublicSetter, "1") + .Set(x => x.SetByCtorWithPrivateSetter, "2") + .Set(x => x.NotSetByCtorWithPrivateSetter, "3") + .Set(x => x.NotSetByCtorWithPublicSetter, "4"); + + dto.SetByCtorWithPublicSetter.ShouldBe("1"); + dto.SetByCtorWithPrivateSetter.ShouldBe("2"); + dto.NotSetByCtorWithPrivateSetter.ShouldBe("3"); + dto.NotSetByCtorWithPublicSetter.ShouldBe("4"); + } + + [Fact] + public void GivenBuilderWithFactoryOverride_WhenBuildingObject_ShouldRespectOverriddenFactory() + { + MixedAccessibilityDto dto = Builder.CreateNew(new CallConstructorFactory()) + .Set(x => x.SetByCtorWithPublicSetter, "1") + .Set(x => x.SetByCtorWithPrivateSetter, "2") + .Set(x => x.NotSetByCtorWithPrivateSetter, "3") + .Set(x => x.NotSetByCtorWithPublicSetter, "4"); + + dto.SetByCtorWithPublicSetter.ShouldBe("1"); + dto.SetByCtorWithPrivateSetter.ShouldBe("2"); + dto.NotSetByCtorWithPrivateSetter.ShouldNotBe("3"); + dto.NotSetByCtorWithPublicSetter.ShouldNotBe("4"); + } + } +} \ No newline at end of file diff --git a/TestStack.Dossier.Tests/Builder_SetUsingBuilderTests.cs b/TestStack.Dossier.Tests/Builder_SetUsingBuilderTests.cs new file mode 100644 index 0000000..ff548b7 --- /dev/null +++ b/TestStack.Dossier.Tests/Builder_SetUsingBuilderTests.cs @@ -0,0 +1,116 @@ +using Shouldly; +using TestStack.Dossier.Lists; +using TestStack.Dossier.Tests.TestHelpers.Builders; +using TestStack.Dossier.Tests.TestHelpers.Objects.ViewModels; +using Xunit; + +namespace TestStack.Dossier.Tests +{ + // ReSharper disable once InconsistentNaming + public class Builder_SetUsingBuilderTests + { + [Fact] + public void GivenBuilderWithObjectPropertyNotSet_WhenBuildingTheObject_ThenThePropertyWillBeNull() + { + var vm = Builder.CreateNew().Build(); + + vm.Address.ShouldBe(null); + } + + [Fact] + public void GivenBuilderWithObjectPropertyNotSet_WhenBuildingAListOfObjects_ThenThePropertyWillBeNull() + { + var vm = Builder.CreateListOfSize(1).BuildList()[0]; + + vm.Address.ShouldBe(null); + } + + [Fact] + public void GivenBuilderWithObjectPropertySetViaBuilder_WhenBuildingTheObject_ThenThePropertyWillBeSet() + { + var vm = Builder.CreateNew() + .SetUsingBuilder(x => x.Address) + .Build(); + + vm.Address.ShouldNotBe(null); + } + + [Fact] + public void GivenBuilderWithObjectPropertySetViaBuilder_WhenBuildingAListOfObjects_ThenThePropertyWillBeSet() + { + var vm = Builder.CreateListOfSize(1) + .TheFirst(1) + .SetUsingBuilder(x => x.Address) + .BuildList()[0]; + + vm.Address.ShouldNotBe(null); + } + + [Fact] + public void GivenBuilderWithObjectPropertySetViaBuilderAndCustomisation_WhenBuildingTheObject_ThenThePropertyWillBeSetIncludingTheCustomisation() + { + var vm = Builder.CreateNew() + .SetUsingBuilder(x => x.Address, b => b.Set(x => x.Street, "A street")) + .Build(); + + vm.Address.ShouldNotBe(null); + vm.Address.Street.ShouldBe("A street"); + } + + [Fact] + public void GivenBuilderWithObjectPropertySetViaBuilderAndCustomisation_WhenBuildingAListOfObjects_ThenThePropertyWillBeSetIncludingTheCustomisation() + { + var vm = Builder.CreateListOfSize(1) + .All() + .SetUsingBuilder(x => x.Address, b => b.Set(x => x.Street, "A street")) + .BuildList()[0]; + + vm.Address.ShouldNotBe(null); + vm.Address.Street.ShouldBe("A street"); + } + + [Fact] + public void GivenBuilderWithObjectPropertySetViaCustomBuilder_WhenBuildingTheObject_ThenThePropertyWillBeSet() + { + var vm = Builder.CreateNew() + .SetUsingBuilder(x => x.Address) + .Build(); + + vm.Address.ShouldNotBe(null); + } + + [Fact] + public void GivenBuilderWithObjectPropertySetViaCustomBuilder_WhenBuildingAListOfObjects_ThenThePropertyWillBeSet() + { + var vm = Builder.CreateListOfSize(1) + .All() + .SetUsingBuilder(x => x.Address) + .BuildList()[0]; + + vm.Address.ShouldNotBe(null); + } + + [Fact] + public void GivenBuilderWithObjectPropertySetViaCustomBuilderAndCustomisation_WhenBuildingTheObject_ThenThePropertyWillBeSetIncludingTheCustomisation() + { + var vm = Builder.CreateNew() + .SetUsingBuilder(x => x.Address, b => b.WithStreet("A street")) + .Build(); + + vm.Address.ShouldNotBe(null); + vm.Address.Street.ShouldBe("A street"); + } + + [Fact] + public void GivenBuilderWithObjectPropertySetViaCustomBuilderAndCustomisation_WhenBuildingAListOfObjects_ThenThePropertyWillBeSetIncludingTheCustomisation() + { + var vm = Builder.CreateListOfSize(1) + .All() + .SetUsingBuilder(x => x.Address, b => b.WithStreet("A street")) + .BuildList()[0]; + + vm.Address.ShouldNotBe(null); + vm.Address.Street.ShouldBe("A street"); + } + } +} diff --git a/TestStack.Dossier.Tests/Builders/BasicCustomerBuilder.cs b/TestStack.Dossier.Tests/Builders/BasicCustomerBuilder.cs deleted file mode 100644 index df39128..0000000 --- a/TestStack.Dossier.Tests/Builders/BasicCustomerBuilder.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System; -using System.Linq.Expressions; -using TestStack.Dossier.Tests.Entities; - -namespace TestStack.Dossier.Tests.Builders -{ - public class BasicCustomerBuilder : TestDataBuilder - { - protected override Customer BuildObject() - { - return new Customer("customer1", "First Name", "Last Name", 2013, CustomerClass.Normal); - } - - public new BasicCustomerBuilder Set(Expression> property, TValue value) - { - return base.Set(property, value); - } - } -} diff --git a/TestStack.Dossier.Tests/ChildBuilderTests.cs b/TestStack.Dossier.Tests/ChildBuilderTests.cs index 3c447bd..7891162 100644 --- a/TestStack.Dossier.Tests/ChildBuilderTests.cs +++ b/TestStack.Dossier.Tests/ChildBuilderTests.cs @@ -68,12 +68,12 @@ public class ParentBuilder : TestDataBuilder { public ParentBuilder() { - Set(x => x.Child, new ChildBuilder().Build()); + Set(x => x.Child, new ChildBuilder()); } public ParentBuilder WithChildBuilder(Func modifier = null) { - return Set(x => x.Child, GetChildBuilder(modifier).Build()); + return Set(x => x.Child, GetChildBuilder(modifier)); } protected override ParentObject BuildObject() diff --git a/TestStack.Dossier.Tests/Factories/AllPropertiesFactoryTests.cs b/TestStack.Dossier.Tests/Factories/AllPropertiesFactoryTests.cs new file mode 100644 index 0000000..cbd8956 --- /dev/null +++ b/TestStack.Dossier.Tests/Factories/AllPropertiesFactoryTests.cs @@ -0,0 +1,41 @@ +using System; +using Shouldly; +using TestStack.Dossier.Factories; +using TestStack.Dossier.Tests.TestHelpers.Objects.Examples; +using Xunit; + +namespace TestStack.Dossier.Tests.Factories +{ + public class AllPropertiesFactoryTests + { + [Fact] + public void GivenAllPropertiesFactory_WhenBuilding_ThenAllPropertiesSet() + { + MixedAccessibilityDto dto = Builder.CreateNew(new AllPropertiesFactory()); + + dto.SetByCtorNoPropertySetter.ShouldNotBe(null); + dto.SetByCtorWithPrivateSetter.ShouldNotBe(null); + dto.SetByCtorWithPublicSetter.ShouldNotBe(null); + dto.NotSetByCtorWithPrivateSetter.ShouldNotBe(null); + dto.NotSetByCtorWithPublicSetter.ShouldNotBe(null); + } + + [Fact] + public void GivenAllPropertiesFactoryAgainstBuilderWithModifications_WhenBuilding_ThenCustomisationsAreUsed() + { + MixedAccessibilityDto dto = Builder + .CreateNew(new AllPropertiesFactory()) + .Set(x => x.SetByCtorNoPropertySetter, "0") + .Set(x => x.SetByCtorWithPrivateSetter, "1") + .Set(x => x.SetByCtorWithPublicSetter, "2") + .Set(x => x.NotSetByCtorWithPrivateSetter, "3") + .Set(x => x.NotSetByCtorWithPublicSetter, "4"); + + dto.SetByCtorNoPropertySetter.ShouldBe("0"); + dto.SetByCtorWithPrivateSetter.ShouldBe("1"); + dto.SetByCtorWithPublicSetter.ShouldBe("2"); + dto.NotSetByCtorWithPrivateSetter.ShouldBe("3"); + dto.NotSetByCtorWithPublicSetter.ShouldBe("4"); + } + } +} diff --git a/TestStack.Dossier.Tests/Factories/AutoFixtureFactoryTests.cs b/TestStack.Dossier.Tests/Factories/AutoFixtureFactoryTests.cs new file mode 100644 index 0000000..899135b --- /dev/null +++ b/TestStack.Dossier.Tests/Factories/AutoFixtureFactoryTests.cs @@ -0,0 +1,40 @@ +using Shouldly; +using TestStack.Dossier.Factories; +using TestStack.Dossier.Tests.TestHelpers.Objects.Examples; +using Xunit; + +namespace TestStack.Dossier.Tests.Factories +{ + public class AutoFixtureFactoryTests + { + [Fact] + public void GivenAutoFixtureFactory_WhenBuilding_ThenOnlyConstructorAndPublicPropertiesSet() + { + MixedAccessibilityDto dto = Builder.CreateNew(new AutoFixtureFactory()); + + dto.SetByCtorNoPropertySetter.ShouldNotBe(null); + dto.SetByCtorWithPrivateSetter.ShouldNotBe(null); + dto.SetByCtorWithPublicSetter.ShouldNotBe(null); + dto.NotSetByCtorWithPrivateSetter.ShouldBe(null); + dto.NotSetByCtorWithPublicSetter.ShouldNotBe(null); + } + + [Fact] + public void GivenAutoFixtureFactoryAgainstBuilderWithModifications_WhenBuilding_ThenNoCustomisationsAreUsed() + { + MixedAccessibilityDto dto = Builder + .CreateNew(new AutoFixtureFactory()) + .Set(x => x.SetByCtorNoPropertySetter, "0") + .Set(x => x.SetByCtorWithPrivateSetter, "1") + .Set(x => x.SetByCtorWithPublicSetter, "2") + .Set(x => x.NotSetByCtorWithPrivateSetter, "3") + .Set(x => x.NotSetByCtorWithPublicSetter, "4"); + + dto.SetByCtorNoPropertySetter.ShouldNotBe("0"); + dto.SetByCtorWithPrivateSetter.ShouldNotBe("1"); + dto.SetByCtorWithPublicSetter.ShouldNotBe("2"); + dto.NotSetByCtorWithPrivateSetter.ShouldNotBe("3"); + dto.NotSetByCtorWithPublicSetter.ShouldNotBe("4"); + } + } +} diff --git a/TestStack.Dossier.Tests/Factories/CallConstructorFactoryTests.cs b/TestStack.Dossier.Tests/Factories/CallConstructorFactoryTests.cs new file mode 100644 index 0000000..61a9d01 --- /dev/null +++ b/TestStack.Dossier.Tests/Factories/CallConstructorFactoryTests.cs @@ -0,0 +1,40 @@ +using Shouldly; +using TestStack.Dossier.Factories; +using TestStack.Dossier.Tests.TestHelpers.Objects.Examples; +using Xunit; + +namespace TestStack.Dossier.Tests.Factories +{ + public class CallConstructorFactoryTests + { + [Fact] + public void GivenCallConstructorFactory_WhenBuilding_ThenOnlyConstructorPropertiesSet() + { + MixedAccessibilityDto dto = Builder.CreateNew(new CallConstructorFactory()); + + dto.SetByCtorNoPropertySetter.ShouldNotBe(null); + dto.SetByCtorWithPrivateSetter.ShouldNotBe(null); + dto.SetByCtorWithPublicSetter.ShouldNotBe(null); + dto.NotSetByCtorWithPrivateSetter.ShouldBe(null); + dto.NotSetByCtorWithPublicSetter.ShouldBe(null); + } + + [Fact] + public void GivenCallConstructorFactoryAgainstBuilderWithModifications_WhenBuilding_ThenCustomisationsAreUsed() + { + MixedAccessibilityDto dto = Builder + .CreateNew(new CallConstructorFactory()) + .Set(x => x.SetByCtorNoPropertySetter, "0") + .Set(x => x.SetByCtorWithPrivateSetter, "1") + .Set(x => x.SetByCtorWithPublicSetter, "2") + .Set(x => x.NotSetByCtorWithPrivateSetter, "3") + .Set(x => x.NotSetByCtorWithPublicSetter, "4"); + + dto.SetByCtorNoPropertySetter.ShouldBe("0"); + dto.SetByCtorWithPrivateSetter.ShouldBe("1"); + dto.SetByCtorWithPublicSetter.ShouldBe("2"); + dto.NotSetByCtorWithPrivateSetter.ShouldBe(null); + dto.NotSetByCtorWithPublicSetter.ShouldBe(null); + } + } +} diff --git a/TestStack.Dossier.Tests/Factories/PublicPropertySettersFactoryTests.cs b/TestStack.Dossier.Tests/Factories/PublicPropertySettersFactoryTests.cs new file mode 100644 index 0000000..3dbe57c --- /dev/null +++ b/TestStack.Dossier.Tests/Factories/PublicPropertySettersFactoryTests.cs @@ -0,0 +1,40 @@ +using Shouldly; +using TestStack.Dossier.Factories; +using TestStack.Dossier.Tests.TestHelpers.Objects.Examples; +using Xunit; + +namespace TestStack.Dossier.Tests.Factories +{ + public class PublicPropertySettersFactoryTests + { + [Fact] + public void GivenPublicPropertySettersFactory_WhenBuilding_ThenOnlyConstructorAndPublicPropertiesSet() + { + MixedAccessibilityDto dto = Builder.CreateNew(new PublicPropertySettersFactory()); + + dto.SetByCtorNoPropertySetter.ShouldNotBe(null); + dto.SetByCtorWithPrivateSetter.ShouldNotBe(null); + dto.SetByCtorWithPublicSetter.ShouldNotBe(null); + dto.NotSetByCtorWithPrivateSetter.ShouldBe(null); + dto.NotSetByCtorWithPublicSetter.ShouldNotBe(null); + } + + [Fact] + public void GivenPublicPropertySettersFactoryAgainstBuilderWithModifications_WhenBuilding_ThenCustomisationsAreUsed() + { + MixedAccessibilityDto dto = Builder + .CreateNew(new PublicPropertySettersFactory()) + .Set(x => x.SetByCtorNoPropertySetter, "0") + .Set(x => x.SetByCtorWithPrivateSetter, "1") + .Set(x => x.SetByCtorWithPublicSetter, "2") + .Set(x => x.NotSetByCtorWithPrivateSetter, "3") + .Set(x => x.NotSetByCtorWithPublicSetter, "4"); + + dto.SetByCtorNoPropertySetter.ShouldBe("0"); + dto.SetByCtorWithPrivateSetter.ShouldBe("1"); + dto.SetByCtorWithPublicSetter.ShouldBe("2"); + dto.NotSetByCtorWithPrivateSetter.ShouldBe(null); + dto.NotSetByCtorWithPublicSetter.ShouldBe("4"); + } + } +} diff --git a/TestStack.Dossier.Tests/GetAnonymousTests.cs b/TestStack.Dossier.Tests/GetAnonymousTests.cs index 442b556..b5a0ddf 100644 --- a/TestStack.Dossier.Tests/GetAnonymousTests.cs +++ b/TestStack.Dossier.Tests/GetAnonymousTests.cs @@ -1,8 +1,9 @@ -using Shouldly; +using System; +using Shouldly; using TestStack.Dossier.DataSources.Person; using TestStack.Dossier.Lists; -using TestStack.Dossier.Tests.Builders; using TestStack.Dossier.Tests.TestHelpers; +using TestStack.Dossier.Tests.TestHelpers.Builders; using Xunit; namespace TestStack.Dossier.Tests @@ -113,9 +114,20 @@ public bool CanSupplyValue(string propertyName) && propertyName.ToLower().StartsWith("year"); } + public bool CanSupplyValue(Type type, string propertyName) + { + return type == typeof(int) + && propertyName.ToLower().StartsWith("year"); + } + public TValue GenerateAnonymousValue(AnonymousValueFixture any, string propertyName) { return (TValue)(object)_year++; } + + public object GenerateAnonymousValue(AnonymousValueFixture any, Type type, string propertyName) + { + return _year++; + } } } diff --git a/TestStack.Dossier.Tests/GetOrDefaultTests.cs b/TestStack.Dossier.Tests/GetOrDefaultTests.cs index 71713a1..5bfbd6b 100644 --- a/TestStack.Dossier.Tests/GetOrDefaultTests.cs +++ b/TestStack.Dossier.Tests/GetOrDefaultTests.cs @@ -1,5 +1,5 @@ using Shouldly; -using TestStack.Dossier.Tests.Builders; +using TestStack.Dossier.Tests.TestHelpers.Builders; using Xunit; namespace TestStack.Dossier.Tests diff --git a/TestStack.Dossier.Tests/GetSetTests.cs b/TestStack.Dossier.Tests/GetSetTests.cs index 4addf96..2362f91 100644 --- a/TestStack.Dossier.Tests/GetSetTests.cs +++ b/TestStack.Dossier.Tests/GetSetTests.cs @@ -1,6 +1,6 @@ using System; using Shouldly; -using TestStack.Dossier.Tests.Builders; +using TestStack.Dossier.Tests.TestHelpers.Builders; using Xunit; namespace TestStack.Dossier.Tests diff --git a/TestStack.Dossier.Tests/ProxyBuilderTests.cs b/TestStack.Dossier.Tests/ProxyBuilderTests.cs index e7aa540..e62b2a5 100644 --- a/TestStack.Dossier.Tests/ProxyBuilderTests.cs +++ b/TestStack.Dossier.Tests/ProxyBuilderTests.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using NSubstitute; using Shouldly; -using TestStack.Dossier.Tests.Entities; +using TestStack.Dossier.Tests.TestHelpers.Objects.Entities; using Xunit; namespace TestStack.Dossier.Tests diff --git a/TestStack.Dossier.Tests/TestHelpers/Builders/AddressViewModelBuilder.cs b/TestStack.Dossier.Tests/TestHelpers/Builders/AddressViewModelBuilder.cs new file mode 100644 index 0000000..512b120 --- /dev/null +++ b/TestStack.Dossier.Tests/TestHelpers/Builders/AddressViewModelBuilder.cs @@ -0,0 +1,18 @@ +using TestStack.Dossier.Factories; +using TestStack.Dossier.Tests.TestHelpers.Objects.ViewModels; + +namespace TestStack.Dossier.Tests.TestHelpers.Builders +{ + public class AddressViewModelBuilder : TestDataBuilder + { + public virtual AddressViewModelBuilder WithStreet(string street) + { + return Set(x => x.Street, street); + } + + protected override AddressViewModel BuildObject() + { + return BuildUsing(); + } + } +} diff --git a/TestStack.Dossier.Tests/TestHelpers/Builders/AutoConstructorCustomerBuilder.cs b/TestStack.Dossier.Tests/TestHelpers/Builders/AutoConstructorCustomerBuilder.cs new file mode 100644 index 0000000..b413c49 --- /dev/null +++ b/TestStack.Dossier.Tests/TestHelpers/Builders/AutoConstructorCustomerBuilder.cs @@ -0,0 +1,28 @@ +using TestStack.Dossier.Factories; +using TestStack.Dossier.Tests.TestHelpers.Objects.Entities; + +namespace TestStack.Dossier.Tests.TestHelpers.Builders +{ + class AutoConstructorCustomerBuilder : TestDataBuilder + { + public AutoConstructorCustomerBuilder WithFirstName(string firstName) + { + return Set(x => x.FirstName, firstName); + } + + public AutoConstructorCustomerBuilder WithLastName(string lastName) + { + return Set(x => x.LastName, lastName); + } + + public AutoConstructorCustomerBuilder WhoJoinedIn(int year) + { + return Set(x => x.YearJoined, year); + } + + protected override Customer BuildObject() + { + return BuildUsing(); + } + } +} diff --git a/TestStack.Dossier.Tests/TestHelpers/Builders/BasicCustomerBuilder.cs b/TestStack.Dossier.Tests/TestHelpers/Builders/BasicCustomerBuilder.cs new file mode 100644 index 0000000..cc3e534 --- /dev/null +++ b/TestStack.Dossier.Tests/TestHelpers/Builders/BasicCustomerBuilder.cs @@ -0,0 +1,12 @@ +using TestStack.Dossier.Tests.TestHelpers.Objects.Entities; + +namespace TestStack.Dossier.Tests.TestHelpers.Builders +{ + public class BasicCustomerBuilder : TestDataBuilder + { + protected override Customer BuildObject() + { + return new Customer("customer1", "First Name", "Last Name", 2013, CustomerClass.Normal); + } + } +} diff --git a/TestStack.Dossier.Tests/Builders/CustomerBuilder.cs b/TestStack.Dossier.Tests/TestHelpers/Builders/CustomerBuilder.cs similarity index 87% rename from TestStack.Dossier.Tests/Builders/CustomerBuilder.cs rename to TestStack.Dossier.Tests/TestHelpers/Builders/CustomerBuilder.cs index 96e133c..1da6a0e 100644 --- a/TestStack.Dossier.Tests/Builders/CustomerBuilder.cs +++ b/TestStack.Dossier.Tests/TestHelpers/Builders/CustomerBuilder.cs @@ -1,6 +1,6 @@ -using TestStack.Dossier.Tests.Entities; +using TestStack.Dossier.Tests.TestHelpers.Objects.Entities; -namespace TestStack.Dossier.Tests.Builders +namespace TestStack.Dossier.Tests.TestHelpers.Builders { public class CustomerBuilder : TestDataBuilder { diff --git a/TestStack.Dossier.Tests/Builders/ProxyAlteringCustomerBuilder.cs b/TestStack.Dossier.Tests/TestHelpers/Builders/ProxyAlteringCustomerBuilder.cs similarity index 84% rename from TestStack.Dossier.Tests/Builders/ProxyAlteringCustomerBuilder.cs rename to TestStack.Dossier.Tests/TestHelpers/Builders/ProxyAlteringCustomerBuilder.cs index 9272dd4..97a156e 100644 --- a/TestStack.Dossier.Tests/Builders/ProxyAlteringCustomerBuilder.cs +++ b/TestStack.Dossier.Tests/TestHelpers/Builders/ProxyAlteringCustomerBuilder.cs @@ -1,8 +1,8 @@ using System; using NSubstitute; -using TestStack.Dossier.Tests.Entities; +using TestStack.Dossier.Tests.TestHelpers.Objects.Entities; -namespace TestStack.Dossier.Tests.Builders +namespace TestStack.Dossier.Tests.TestHelpers.Builders { class ProxyAlteringCustomerBuilder : TestDataBuilder { diff --git a/TestStack.Dossier.Tests/Entities/Company.cs b/TestStack.Dossier.Tests/TestHelpers/Objects/Entities/Company.cs similarity index 83% rename from TestStack.Dossier.Tests/Entities/Company.cs rename to TestStack.Dossier.Tests/TestHelpers/Objects/Entities/Company.cs index 4e2dfbe..df96a43 100644 --- a/TestStack.Dossier.Tests/Entities/Company.cs +++ b/TestStack.Dossier.Tests/TestHelpers/Objects/Entities/Company.cs @@ -1,4 +1,4 @@ -namespace TestStack.Dossier.Tests.Entities +namespace TestStack.Dossier.Tests.TestHelpers.Objects.Entities { public class Company { diff --git a/TestStack.Dossier.Tests/Entities/Customer.cs b/TestStack.Dossier.Tests/TestHelpers/Objects/Entities/Customer.cs similarity index 94% rename from TestStack.Dossier.Tests/Entities/Customer.cs rename to TestStack.Dossier.Tests/TestHelpers/Objects/Entities/Customer.cs index 5796b0d..3c943ea 100644 --- a/TestStack.Dossier.Tests/Entities/Customer.cs +++ b/TestStack.Dossier.Tests/TestHelpers/Objects/Entities/Customer.cs @@ -1,6 +1,6 @@ -using System; +using System; -namespace TestStack.Dossier.Tests.Entities +namespace TestStack.Dossier.Tests.TestHelpers.Objects.Entities { public class Customer { diff --git a/TestStack.Dossier.Tests/Entities/CustomerClass.cs b/TestStack.Dossier.Tests/TestHelpers/Objects/Entities/CustomerClass.cs similarity index 65% rename from TestStack.Dossier.Tests/Entities/CustomerClass.cs rename to TestStack.Dossier.Tests/TestHelpers/Objects/Entities/CustomerClass.cs index 81f3956..aa3a080 100644 --- a/TestStack.Dossier.Tests/Entities/CustomerClass.cs +++ b/TestStack.Dossier.Tests/TestHelpers/Objects/Entities/CustomerClass.cs @@ -1,4 +1,4 @@ -namespace TestStack.Dossier.Tests.Entities +namespace TestStack.Dossier.Tests.TestHelpers.Objects.Entities { public enum CustomerClass { diff --git a/TestStack.Dossier.Tests/TestHelpers/Objects/Examples/MixedAccessibilityDto.cs b/TestStack.Dossier.Tests/TestHelpers/Objects/Examples/MixedAccessibilityDto.cs new file mode 100644 index 0000000..99cb17a --- /dev/null +++ b/TestStack.Dossier.Tests/TestHelpers/Objects/Examples/MixedAccessibilityDto.cs @@ -0,0 +1,26 @@ +namespace TestStack.Dossier.Tests.TestHelpers.Objects.Examples +{ + public class MixedAccessibilityDto + { + private readonly string _setByCtorNoPropertySetter; + + public MixedAccessibilityDto(string setByCtorWithPrivateSetter, string setByCtorWithPublicSetter, string setByCtorNoPropertySetter) + { + SetByCtorWithPrivateSetter = setByCtorWithPrivateSetter; + SetByCtorWithPublicSetter = setByCtorWithPublicSetter; + _setByCtorNoPropertySetter = setByCtorNoPropertySetter; + } + + public string SetByCtorWithPrivateSetter { get; private set; } + public string SetByCtorWithPublicSetter { get; set; } + + // ReSharper disable once UnusedAutoPropertyAccessor.Local + public string NotSetByCtorWithPrivateSetter { get; private set; } + public string NotSetByCtorWithPublicSetter { get; set; } + + public string SetByCtorNoPropertySetter + { + get { return _setByCtorNoPropertySetter; } + } + } +} \ No newline at end of file diff --git a/TestStack.Dossier.Tests/TestHelpers/Objects/ViewModels/AddressViewModel.cs b/TestStack.Dossier.Tests/TestHelpers/Objects/ViewModels/AddressViewModel.cs new file mode 100644 index 0000000..7a24c75 --- /dev/null +++ b/TestStack.Dossier.Tests/TestHelpers/Objects/ViewModels/AddressViewModel.cs @@ -0,0 +1,8 @@ +namespace TestStack.Dossier.Tests.TestHelpers.Objects.ViewModels +{ + public class AddressViewModel + { + public string Street { get; set; } + public string Suburb { get; set; } + } +} \ No newline at end of file diff --git a/TestStack.Dossier.Tests/TestHelpers/Objects/ViewModels/Grade.cs b/TestStack.Dossier.Tests/TestHelpers/Objects/ViewModels/Grade.cs new file mode 100644 index 0000000..2594136 --- /dev/null +++ b/TestStack.Dossier.Tests/TestHelpers/Objects/ViewModels/Grade.cs @@ -0,0 +1,7 @@ +namespace TestStack.Dossier.Tests.TestHelpers.Objects.ViewModels +{ + public enum Grade + { + A, B, C, D, F + } +} \ No newline at end of file diff --git a/TestStack.Dossier.Tests/TestHelpers/Objects/ViewModels/StudentViewModel.cs b/TestStack.Dossier.Tests/TestHelpers/Objects/ViewModels/StudentViewModel.cs new file mode 100644 index 0000000..63e6017 --- /dev/null +++ b/TestStack.Dossier.Tests/TestHelpers/Objects/ViewModels/StudentViewModel.cs @@ -0,0 +1,37 @@ +using System; +using System.ComponentModel.DataAnnotations; + +namespace TestStack.Dossier.Tests.TestHelpers.Objects.ViewModels +{ + public class StudentViewModel + { + public int Id { get; set; } + + [Required] + [StringLength(50)] + [Display(Name = "Last Name")] + public string LastName { get; set; } + [Required] + [StringLength(50, ErrorMessage = "First name cannot be longer than 50 characters.")] + [Display(Name = "First Name")] + public string FirstName { get; set; } + + [Display(Name = "Full Name")] + public string FullName + { + get + { + return FirstName + " " + LastName; + } + } + + [DataType(DataType.Date)] + [DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)] + [Display(Name = "Enrollment Date")] + public DateTime EnrollmentDate { get; set; } + + public Grade Grade { get; set; } + + public AddressViewModel Address { get; set; } + } +} diff --git a/TestStack.Dossier.Tests/TestHelpers/StaticAnonymousValueSupplier.cs b/TestStack.Dossier.Tests/TestHelpers/StaticAnonymousValueSupplier.cs index dc3db91..4c7c9a0 100644 --- a/TestStack.Dossier.Tests/TestHelpers/StaticAnonymousValueSupplier.cs +++ b/TestStack.Dossier.Tests/TestHelpers/StaticAnonymousValueSupplier.cs @@ -1,4 +1,6 @@ -namespace TestStack.Dossier.Tests.TestHelpers +using System; + +namespace TestStack.Dossier.Tests.TestHelpers { public class StaticAnonymousValueSupplier : IAnonymousValueSupplier { @@ -14,9 +16,19 @@ public bool CanSupplyValue(string propertyName) return typeof(TValue) == _valueToSupply.GetType(); } + public bool CanSupplyValue(Type type, string propertyName) + { + return type == _valueToSupply.GetType(); + } + public TValue GenerateAnonymousValue(AnonymousValueFixture any, string propertyName) { return (TValue) _valueToSupply; } + + public object GenerateAnonymousValue(AnonymousValueFixture any, Type type, string propertyName) + { + return _valueToSupply; + } } } diff --git a/TestStack.Dossier.Tests/TestStack.Dossier.Tests.csproj b/TestStack.Dossier.Tests/TestStack.Dossier.Tests.csproj index 73d099b..380dd50 100644 --- a/TestStack.Dossier.Tests/TestStack.Dossier.Tests.csproj +++ b/TestStack.Dossier.Tests/TestStack.Dossier.Tests.csproj @@ -13,7 +13,7 @@ 512 ..\ - d4853673 + 660882d1 true @@ -44,6 +44,7 @@ ..\packages\Shouldly.2.4.0\lib\net40\Shouldly.dll + @@ -54,8 +55,19 @@ + + + + + - + + + + + + + @@ -71,17 +83,19 @@ - - - + + + - - + + + + @@ -89,17 +103,20 @@ Designer + + + PreserveNewest + + + + + {01e4ee61-ab1a-4177-8b6c-d50205d167a9} TestStack.Dossier - - - PreserveNewest - -