roryprimrose / modelbuilder Goto Github PK
View Code? Open in Web Editor NEWA library for easy creation and data population of model classes for testing
License: MIT License
A library for easy creation and data population of model classes for testing
License: MIT License
Currently the parameters for a constructor are generated within the loop of the identified parameters on a constructor. This set of parameters could be created by applying the ExecuteOrderRules on the build configuration so that related data could be populated and then resorting them to their constructor parameter order
The signature of the IsMatch function can use PropertyInfo rather than Type and string.
This will be a breaking change.
The DefaultBuildStrategyCompiler uses type on ExecuteOrderRules to identify the ordering of evaluation by property type. The DefaultExecuteStrategy uses the rules against the property type so this works as intended.
The expression extension methods however use the owning type of the property to register a rule. This work syntactically but is very broken when the rule is used by DefaultExecuteStrategy.
For example:
public static partial class Create
{
public static T Model<T>()
{
var compiler = ModelBuilder.Model.BuildStrategy.Clone();
compiler.AddExecuteOrderRule<Skill>(x => x.YearStarted, 4000);
compiler.AddExecuteOrderRule<Skill>(x => x.YearLastUsed, 3000);
compiler.AddValueGenerator<YearInPastValueGenerator>();
var builder = compiler.Compile();
return builder.Create<T>();
}
}
This should add order rules such that YearStarted is evaluated first and YearLastUsed is evaluated afterwards. The bug is that AddExecuteOrderRule creates an order rule against typeof(Skill) instead of typeof(int?) which is the property type determined from the expression.
Both scenarios are valid cases. There should be the option to target the ExecuteOrderRule against a property type as well as owning type of the property. This would cater for being able to run strings after enums for example, while also using expressions to target specific properties.
Some of the types in the new refactor do not have supported extension methods on IBuildConfiguration. For example, you can only add a creation rule using an expression but not regex or predicate. Other types of extensibility points are also missing overloads
Consider the following:
Model.Ignoring<ValueTypeException>(x => x.Data).Create<ValueTypeException>();
This should already know the type being created such that it can be written as
Model.Ignoring<ValueTypeException>(x => x.Data).Create();
and still have it being strongly typed.
Creating a model that contains public string cultureName { get; set; }
has a guid assigned to the property rather than a culture name.
DefaultExecuteStrategy does not populate a public read-only (no setter) property of an instance class that has a value returned for it. The issue here is that PropertyInfo.CanWrite returns false if there is no setter whereas it returns true if there is a private setter. A property that is truly read-only is excluded from the instance population logic.
The code at https://github.com/roryprimrose/ModelBuilder/blob/master/ModelBuilder/DefaultExecuteStrategy.cs#L259 should have better logic for filtering out unsupported properties.
An example would be that a property or parameter is either an abstract class or an interface. Add fluent support and builder strategy support for using a mapping of types that the execute strategy can use when creating the values
The current interface design uses (Type, string) signatures to identity scenarios of
Writing custom implementations of these interfaces can be very confusing to understand how to target specific scenarios. For example, a property that is being evaluated, is the type the type that declares the property or the return type of the property.
Updating these signatures will solve this confusion by providing the following overloads
There needs to be extension methods on IBuildConfiguration to allow configuration of any instances held in a collection. This will include ValueGenerators, TypeCreators, IgnoreRules, CreationRules etc.
For example, the idea behind ValueGenerators exposing static properties was a single place to control configuration of the ValueGenerator. This is going to change to use instance properties where extension methods on IBuildConfiguration will allow for configuring those types.
Constructors can be cached when no arguments are supplied that require matching
Parsing the embedded resources uses Environment.NewLine
which is fine on non-Unix platforms because the files using CRLF as the new line delimiter. This will not work on Unix however as it uses LF as the new line delimiter but the embedded file is still delimited by CRLF
If an IEnumerable<T>
is passed to SetEach
then the instance returned is the same IEnumerable. This would cause a potential multiple enumeration problem. In this case the code should use yield
. If it is something other than strictly IEnumerable<T>
then it should return the original parameter value.
The logic for creating the root level instance skips assignments to properties that can be matched to the constructor parameters provided for the root create request. This logic is not applied to creating nested types using constructor parameters under the root.
Array uses random 1 to max whereas Enumerable uses AutoPopulateCount. Both of these should support a random number between a min and max where the defaults are something like 10 and 20
Oops
Some properties use other properties to help define their values. For example, an object that has first name and last name uses these values to help populate an email property. The source of these values is from a data set stored as an embedded resource. This resource has 1000 entries.
The issue is that a single record from that dataset is used. There may be 1000 unique first names and 1000 unique last names. The single record means that there are only 1000 unique combinations that could be determined rather than 1000 x 1000.
This applies to many of the other property values as well. A better job could be done by providing better combinations of predefined random values. Primarily this would be done by splitting up groups of information:
If a different type is created than what was requested, this is not reflected in the build log. The IBuildLog.MappedType
method is never called.
Implement app domain scanning of types to look for an interface that will support the configuration of a build strategy. This will decouple the logic of configuring custom build strategies from the Create static class.
Some considerations in designing this will be:
This is an issue because the return type does not match the original type
The current constraint is that BuildStrategy must provide all information. This is clunky when you want to take something like a strategy from the default compiler and then tweak it. The solution here would be to at least add a IBuildStrategy copy constructor.
The current implementations of BuildStrategy and compilation is overly complex. This should be streamlined to allow for a simple build configuration that will drive the object generation process. The one sacrifice in this change of design will be that the build configuration will no longer be immutable after compilation from a BuildStrategy.
Creating a reference type or a value type should have access to the execute strategy rather than just the build chain. One use case here is that a type creator may need access to the build configuration to use the constructor resolver. Currently this is not possible.
Debugging value creation is difficult because the build log does not indicate which types were involved.
Trying to get a int? value from another property throws an exception when the other property has a value.
Creating property YearLastUsed (System.Nullable`1[[System.Int32, System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]]) on type DevMentorApi.Model.Skill
Creating System.Nullable`1[[System.Int32, System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]] value using DevMentorApi.AcceptanceTests.YearInPastValueGenerator
System.InvalidCastException: Invalid cast from 'System.Int32' to 'System.Nullable`1[[System.Int32, System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]]'.
at System.Convert.DefaultToType(IConvertible value, Type targetType, IFormatProvider provider)
at ModelBuilder.RelativeValueGenerator.GetValue[T](Regex expression, Object context) in C:\Repos\ModelBuilder\ModelBuilder\RelativeValueGenerator.cs:line 159
IPropertyResolver should include a GetProperties member that also takes care of ordering as per ExecuteOrderRules.
Getting properties and ordering how they are evaluated should be moved from DefaultExecuteStrategy to DefaultPropertyResolver
The following exception is thrown when TimeZoneValueGenerator is building an instance that has a Country value that does not exist in any time zone name.
Failed to create value for type System.String using value generator ModelBuilder.TimeZoneValueGenerator, ArgumentOutOfRangeException: Specified argument was out of the range of valid values.
Parameter name: min
At the time of the failure, the build log was:
Start creating type System.Collections.Generic.List`1[[TestingDemo.Model.Person, TestingDemo.Model, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]
Start creating type TestingDemo.Model.Person
Start populating instance TestingDemo.Model.Person
Creating property Gender (System.String) on type TestingDemo.Model.Person
Creating System.String value
Creating property FirstName (System.String) on type TestingDemo.Model.Person
Creating System.String value
Creating property LastName (System.String) on type TestingDemo.Model.Person
Creating System.String value
Creating property Email (System.String) on type TestingDemo.Model.Person
Creating System.String value
Creating property Address (System.String) on type TestingDemo.Model.Person
Creating System.String value
Creating property City (System.String) on type TestingDemo.Model.Person
Creating System.String value
Creating property Company (System.String) on type TestingDemo.Model.Person
Creating System.String value
Creating property Country (System.String) on type TestingDemo.Model.Person
Creating System.String value
Creating property Domain (System.String) on type TestingDemo.Model.Person
Creating System.String value
Creating property Phone (System.String) on type TestingDemo.Model.Person
Creating System.String value
Creating property PostCode (System.String) on type TestingDemo.Model.Person
Creating System.String value
Creating property State (System.String) on type TestingDemo.Model.Person
Creating System.String value
Creating property TimeZone (System.String) on type TestingDemo.Model.Person
Creating System.String value
System.ArgumentOutOfRangeException: Specified argument was out of the range of valid values.
Parameter name: min
at ModelBuilder.RandomGenerator.NextValue[T](Double min, Double max, Boolean roundValue) in C:\\Repos\\ModelBuilder\\ModelBuilder\\RandomGenerator.cs:line 289
at ModelBuilder.RandomGenerator.NextValue(Type type, Object min, Object max) in C:\\Repos\\ModelBuilder\\ModelBuilder\\RandomGenerator.cs:line 234
at ModelBuilder.RandomGeneratorExtensions.NextValue[T](IRandomGenerator generator, T min, T max) in C:\\Repos\\ModelBuilder\\ModelBuilder\\RandomGeneratorExtensions.cs:line 69
at ModelBuilder.TimeZoneValueGenerator.GenerateValue(Type type, String referenceName, LinkedList`1 buildChain) in C:\\Repos\\ModelBuilder\\ModelBuilder\\TimeZoneValueGenerator.cs:line 51
at ModelBuilder.ValueGeneratorBase.Generate(Type type, String referenceName, LinkedList`1 buildChain) in C:\\Repos\\ModelBuilder\\ModelBuilder\\ValueGeneratorBase.cs:line 21
at ModelBuilder.DefaultExecuteStrategy.Build(Type type, String referenceName, Object context, Object[] args) in C:\\Repos\\ModelBuilder\\ModelBuilder\\DefaultExecuteStrategy.cs:line 158
The following exception was thrown building a class.
System.ArgumentOutOfRangeException
Index was out of range. Must be non-negative and less than the size of the collection.
Parameter name: chunkLength
at System.Text.StringBuilder.ToString()
at ModelBuilder.DefaultExecuteStrategy.Build(Type type, String referenceName, Object context, Object[] args) in C:\Repos\ModelBuilder\ModelBuilder\DefaultExecuteStrategy.cs:line 198
at ModelBuilder.DefaultExecuteStrategy`1.CreateWith(Object[] args) in C:\Repos\ModelBuilder\ModelBuilder\DefaultExecuteStrategyT.cs:line 22
The DateTimeValueGenerator is creating values that cannot be supported by DateTime, DateTimeOffset and TimeSpan.
System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.ArgumentOutOfRangeException: Ticks must be between DateTime.MinValue.Ticks and DateTime.MaxValue.Ticks.
Parameter name: ticks
at System.DateTime..ctor(Int64 ticks)
at System.DateTimeOffset..ctor(Int64 ticks, TimeSpan offset)
There needs to be some constraints around the dates to ensure random shift values don't go too far.
The issue here is that if TypeA has a property of TypeB and TypeB has a property of TypeA, ModelBuilder will continue to recursively create the relationships until either a StackOverflowException or OutOfMemoryException is thrown.
The following test should populate an existing instance correctly.
[Fact]
public void PopulateUsesResolvedTypeCreatorToPopulateInstanceTest()
{
var actual = new List<Person>();
actual = Model.Populate(actual);
actual.Should().NotBeEmpty();
actual.Count.Should().Be(EnumerableTypeCreator.DefaultAutoPopulateCount);
}
The future progression of build systems is leaving the full .net framework behind. It's time to say goodbye to the support for the full .net framework.
Struct not strict
There can be situations where a type creator cannot create an instance of a type, but it can populate it. This is proven by having a read-only IEnumerable property. EnumerableTypeCreator currently says that it is supported, but that is only taking into consideration creating the instance. If a initialized read-only instance is already provided then this fails because IsSupported returns true, but it should be false because IEnumerable can't be populated unless the underlying type can be converted to ICollection.
The AgeValueGenerator currently creates a numeric value between 1 and 100 by default. This could be updated so that the age was based on a DOB value if one was already assigned.
This change requires an update to the ExecuteOrderRules and the AgeValueGenerator.
The constructor resolver looks for constructors that have a parameter type set that matches the arguments. This fails when one of the argument is null.
There should be an option to configure ValueGenerators that return Nullable to toggle whether null can be allowed as a generated value.
TypeMapping needs to move into an ITypeResolver and move from DefaultExecuteStrategy down to TypeCreator IBuildAction
Building a model that assigns property values from constructor parameters should skip over those properties. The logic for building property values should be something like:
The current implementation will only populate writable fields. This is because population occurs after construction but there is no point constructing read-only properties. This should be updated so that read-only fields are populated even though the execution strategy did not create them.
Invoking Model.Create can fail when assembly scanning for ICompilerModule if an assembly in the AppDomain fails be to loaded.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.