Skip to content

Commit

Permalink
Added FactoryTypeCreator
Browse files Browse the repository at this point in the history
Updated DefaultConstructorResolver to return null when no constructor found
Updated DefaultTypeCreator to not create types without a public constructors
  • Loading branch information
roryprimrose committed Jun 13, 2020
1 parent 5623b01 commit 59df0f1
Show file tree
Hide file tree
Showing 16 changed files with 655 additions and 171 deletions.
64 changes: 11 additions & 53 deletions ModelBuilder.UnitTests/DefaultConstructorResolverTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -207,12 +207,20 @@ public void ResolveReturnsDefaultConstructorWhenManyConstructorsAvailable()
constructor.GetParameters().Should().BeEmpty();
}

[Fact]
public void ResolveReturnsNullForStructThatHasNoConstructors()
[Theory]
[InlineData(typeof(StructModel))]
[InlineData(typeof(FactoryItem))]
[InlineData(typeof(FactoryWithValue))]
[InlineData(typeof(NotFactoryItem))]
[InlineData(typeof(Singleton))]
[InlineData(typeof(Gender))]
[InlineData(typeof(Copy))]
[InlineData(typeof(Clone))]
public void ResolveReturnsNullForTypesWithoutPublicConstructors(Type targetType)
{
var sut = new DefaultConstructorResolver(CacheLevel.PerInstance);

var constructor = sut.Resolve(typeof(StructModel));
var constructor = sut.Resolve(targetType);

constructor.Should().BeNull();
}
Expand All @@ -227,16 +235,6 @@ public void ResolveReturnsParameterConstructor()
constructor.GetParameters().Should().NotBeEmpty();
}

[Fact]
public void ResolveThrowsExceptionForEnum()
{
var sut = new DefaultConstructorResolver(CacheLevel.PerInstance);

Action action = () => sut.Resolve(typeof(Gender));

_output.WriteLine(action.Should().Throw<MissingMemberException>().And.Message);
}

[Fact]
public void ResolveThrowsExceptionWhenArgsContainsNullAndArgCountLessThanOptionalParam()
{
Expand Down Expand Up @@ -315,26 +313,6 @@ public void ResolveThrowsExceptionWhenArgsContainsNullNoOptionalParamsAndArgumen
_output.WriteLine(action.Should().Throw<MissingMemberException>().And.Message);
}

[Fact]
public void ResolveThrowsExceptionWhenClassOnlyContainsConstructorsThatReferenceTheSameType()
{
var sut = new DefaultConstructorResolver(CacheLevel.PerInstance);

Action action = () => sut.Resolve(typeof(Clone));

_output.WriteLine(action.Should().Throw<MissingMemberException>().And.Message);
}

[Fact]
public void ResolveThrowsExceptionWhenClassOnlyContainsCopyConstructor()
{
var sut = new DefaultConstructorResolver(CacheLevel.PerInstance);

Action action = () => sut.Resolve(typeof(Copy));

_output.WriteLine(action.Should().Throw<MissingMemberException>().And.Message);
}

[Fact]
public void ResolveThrowsExceptionWhenNoConstructorMatchingSpecifiedParameters()
{
Expand All @@ -345,16 +323,6 @@ public void ResolveThrowsExceptionWhenNoConstructorMatchingSpecifiedParameters()
_output.WriteLine(action.Should().Throw<MissingMemberException>().And.Message);
}

[Fact]
public void ResolveThrowsExceptionWhenNoPublicConstructorFound()
{
var sut = new DefaultConstructorResolver(CacheLevel.PerInstance);

Action action = () => sut.Resolve(typeof(Singleton));

_output.WriteLine(action.Should().Throw<MissingMemberException>().And.Message);
}

[Fact]
public void ResolveThrowsExceptionWhenParameterValuesDoNotMatchParameterTypes()
{
Expand All @@ -374,16 +342,6 @@ public void ResolveThrowsExceptionWhenParameterValuesDoNotMatchParameterTypes()
_output.WriteLine(action.Should().Throw<MissingMemberException>().And.Message);
}

[Fact]
public void ResolveThrowsExceptionWhenWhenOnlyPrivateConstructorAvailable()
{
var sut = new DefaultConstructorResolver(CacheLevel.PerInstance);

Action action = () => sut.Resolve(typeof(Singleton));

_output.WriteLine(action.Should().Throw<MissingMemberException>().And.Message);
}

[Fact]
public void ResolveThrowsExceptionWithNullType()
{
Expand Down
76 changes: 76 additions & 0 deletions ModelBuilder.UnitTests/DefaultExecuteStrategyTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,49 @@ public void ConfigurationThrowsExceptionWhenNotInitialized()
action.Should().Throw<InvalidOperationException>();
}

[Fact]
public void CreateCanAssignNullProperty()
{
var buildHistory = new BuildHistory();
var expected = new NullablePropertyModel<Person>();

var processor = Substitute.For<IBuildProcessor>();
var buildConfiguration = Substitute.For<IBuildConfiguration>();
var propertyResolver = Substitute.For<IPropertyResolver>();
var typeCapability = Substitute.For<IBuildCapability>();
var valueCapability = Substitute.For<IBuildCapability>();

typeCapability.AutoPopulate.Returns(true);
typeCapability.SupportsCreate.Returns(true);
typeCapability.SupportsPopulate.Returns(true);
typeCapability.ImplementedByType.Returns(typeof(DummyTypeCreator));
valueCapability.SupportsCreate.Returns(true);
valueCapability.ImplementedByType.Returns(typeof(DummyTypeCreator));

var sut = new DefaultExecuteStrategy(buildHistory, _buildLog, processor);

processor.GetBuildCapability(sut, Arg.Any<BuildRequirement>(),
typeof(NullablePropertyModel<Person>))
.Returns(typeCapability);
processor.GetBuildCapability(sut, Arg.Any<BuildRequirement>(),
Arg.Any<PropertyInfo>())
.Returns(valueCapability);
typeCapability.CreateType(sut, typeof(NullablePropertyModel<Person>), Arg.Any<object[]>())
.Returns(expected);
valueCapability.CreateProperty(sut, Arg.Any<PropertyInfo>(), Arg.Any<object[]>()).Returns(null);
typeCapability.Populate(sut, expected).Returns(expected);
buildConfiguration.PropertyResolver.Returns(propertyResolver);
propertyResolver.GetOrderedProperties(buildConfiguration, typeof(NullablePropertyModel<Person>))
.Returns(typeof(NullablePropertyModel<Person>).GetProperties());

sut.Initialize(buildConfiguration);

var actual = (NullablePropertyModel<Person>) sut.Create(typeof(NullablePropertyModel<Person>))!;

actual.Should().Be(expected);
actual.Value.Should().BeNull();
}

[Fact]
public void CreateDeterminesPropertiesToCreateByProvidingConstructorArgsForNestedType()
{
Expand Down Expand Up @@ -374,6 +417,39 @@ public void CreateEvaluatesPostBuildActionsWhenCapabilityDoesNotSupportPopulatio
action.Received().Execute(Arg.Any<IBuildChain>(), Arg.Any<SlimModel>(), typeof(SlimModel));
}

[Fact]
public void CreateParametersCanReturnNullParameter()
{
var buildHistory = new BuildHistory();
var method = typeof(SimpleConstructor).GetConstructors().First();
var parameters = method.GetParameters().OrderBy(x => x.Name);

var parameterCapability = Substitute.For<IBuildCapability>();
var processor = Substitute.For<IBuildProcessor>();
var parameterResolver = Substitute.For<IParameterResolver>();
var buildConfiguration = Substitute.For<IBuildConfiguration>();

var sut = new DefaultExecuteStrategy(buildHistory, _buildLog, processor);

parameterCapability.AutoPopulate.Returns(false);
parameterCapability.SupportsCreate.Returns(true);
parameterCapability.SupportsPopulate.Returns(false);
parameterCapability.AutoDetectConstructor.Returns(false);
parameterCapability.ImplementedByType.Returns(GetType());
parameterCapability.CreateParameter(sut, Arg.Any<ParameterInfo>(), null).Returns(null);
processor.GetBuildCapability(sut, BuildRequirement.Create,
Arg.Any<ParameterInfo>()).Returns(parameterCapability);
parameterResolver.GetOrderedParameters(buildConfiguration, method).Returns(parameters);
buildConfiguration.ParameterResolver.Returns(parameterResolver);

sut.Initialize(buildConfiguration);

var actual = sut.CreateParameters(method)!;

actual.Should().HaveCount(1);
actual[0].Should().BeNull();
}

[Fact]
public void CreateParametersReturnsNullWhenNoOrderedParametersReturned()
{
Expand Down
19 changes: 19 additions & 0 deletions ModelBuilder.UnitTests/Models/FactoryItem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
namespace ModelBuilder.UnitTests.Models
{
using System;

public class FactoryItem
{
private FactoryItem()
{
Value = Guid.NewGuid();
}

public static FactoryItem Create()
{
return new FactoryItem();
}

public Guid Value { get; }
}
}
19 changes: 19 additions & 0 deletions ModelBuilder.UnitTests/Models/FactoryWithValue.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
namespace ModelBuilder.UnitTests.Models
{
using System;

public class FactoryWithValue
{
private FactoryWithValue(Guid value)
{
Value = value;
}

public static FactoryWithValue Create(Guid value)
{
return new FactoryWithValue(value);
}

public Guid Value { get; }
}
}
19 changes: 19 additions & 0 deletions ModelBuilder.UnitTests/Models/NotFactoryItem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
namespace ModelBuilder.UnitTests.Models
{
using System;

public class NotFactoryItem
{
private NotFactoryItem()
{
Value = Guid.NewGuid();
}

public static NotFactoryItem Create(NotFactoryItem item)
{
return item;
}

public Guid Value { get; }
}
}
7 changes: 7 additions & 0 deletions ModelBuilder.UnitTests/Models/NullablePropertyModel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace ModelBuilder.UnitTests.Models
{
public class NullablePropertyModel<T> where T : class
{
public T Value { get; set; } = default!;
}
}
23 changes: 0 additions & 23 deletions ModelBuilder.UnitTests/Scenarios/ConstructorTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,6 @@ public ConstructorTests(ITestOutputHelper output)
_output = output;
}

[Fact]
public void CreateThrowsExceptionWhenNoPublicConstructorFound()
{
Action action = () => Model.Create<FactoryClass>();

action.Should().Throw<BuildException>();
}

[Fact]
public void CreateReturnsTypeThatDefinesCopyConstructor()
{
Expand Down Expand Up @@ -90,20 +82,5 @@ public void PopulatesValueTypePropertyWhenConstructorParameterMatchesDefaultType

model.Id.Should().NotBeEmpty();
}

private class FactoryClass
{
private FactoryClass(Guid value)
{
Value = value;
}

public static FactoryClass Create(Guid value)
{
return new FactoryClass(value);
}

public Guid Value { get; }
}
}
}
36 changes: 36 additions & 0 deletions ModelBuilder.UnitTests/Scenarios/NonConstructorCreationTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
namespace ModelBuilder.UnitTests.Scenarios
{
using System;
using FluentAssertions;
using ModelBuilder.UnitTests.Models;
using Xunit;

public class NonConstructorCreationTests
{
[Fact]
public void CanCreateViaStaticFactoryMethodWithCreatedParameters()
{
var actual = Model.Create<FactoryWithValue>();

actual.Value.Should().NotBeEmpty();
}

[Fact]
public void CanCreateViaStaticFactoryMethodWithoutParameters()
{
var actual = Model.Create<FactoryItem>();

actual.Value.Should().NotBeEmpty();
}

[Fact]
public void CanCreateViaStaticFactoryMethodWithProvidedParameters()
{
var value = Guid.NewGuid();

var actual = Model.Create<FactoryWithValue>(value);

actual.Value.Should().Be(value);
}
}
}
Loading

0 comments on commit 59df0f1

Please sign in to comment.