Browse files

Merge branch 'array-support'

  • Loading branch information...
2 parents 99ad992 + 18f805e commit a889791a6a5ad8259920e9b92698f27a3be46883 @Aaronontheweb committed Mar 18, 2012
Showing with 189 additions and 28 deletions.
  1. +1 −0 Faker.Tests/Faker.Tests.csproj
  2. +110 −0 Faker.Tests/MatcherTests/ListMatcherTests.cs
  3. +24 −4 Faker/Fake.cs
  4. +54 −24 Faker/Matcher.cs
View
1 Faker.Tests/Faker.Tests.csproj
@@ -57,6 +57,7 @@
<Compile Include="GeneratorTests\NameGeneratorTests.cs" />
<Compile Include="GeneratorTests\NumberGeneratorTests.cs" />
<Compile Include="GeneratorTests\StringsGeneratorTests.cs" />
+ <Compile Include="MatcherTests\ListMatcherTests.cs" />
<Compile Include="MatcherTests\NestedPocoMatcherTests.cs" />
<Compile Include="MatcherTests\SimplePocoMatcherTests.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
View
110 Faker.Tests/MatcherTests/ListMatcherTests.cs
@@ -0,0 +1,110 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Text.RegularExpressions;
+using NUnit.Framework;
+
+namespace Faker.Tests.MatcherTests
+{
+ [TestFixture(Description = "Tests for validating that our matcher is able to ")]
+ public class ListMatcherTests
+ {
+ public const string ValidEmailRegex = @"(\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}\b)";
+
+ public Regex _valid_email_regex = new Regex(ValidEmailRegex, RegexOptions.IgnoreCase);
+
+ private Matcher _matcher;
+
+ #region List test classes
+
+ public class SpecialFieldsTestClass
+ {
+ public int UserID { get; set; }
+ public string Name { get; set; }
+ public string Email { get; set; }
+ public long Timestamp { get; set; }
+ public DateTime DateRegistered { get; set; }
+ }
+
+ public class SimpleListClass
+ {
+ public List<string> strings { get; set; }
+ public List<DateTime> dates { get; set; }
+ }
+
+ public class PocoListsClass
+ {
+ public List<SpecialFieldsTestClass> Users { get; set; }
+ }
+
+ #endregion
+
+ #region Setup / Teardown
+
+ [SetUp]
+ public void SetUp()
+ {
+ _matcher = new Matcher();
+ }
+
+ #endregion
+
+ #region Tests
+
+ [Test(Description = "Should be able to inject and inject values for lists of primitive elements, even with the lists currently set to null")]
+ public void Should_Bind_Primitive_Lists_With_Uninstantiated_Target_Objects()
+ {
+ var simpleListClass = new SimpleListClass();
+
+ _matcher.Match(simpleListClass);
+
+ Assert.IsNotNull(simpleListClass.dates);
+ Assert.IsNotNull(simpleListClass.strings);
+ Assert.IsTrue(simpleListClass.dates.Count > 0);
+ Assert.IsTrue(simpleListClass.strings.Count > 0);
+ Assert.IsTrue(simpleListClass.dates.All(x => x != default(DateTime)));
+ Assert.IsTrue(simpleListClass.strings.All(x => x != default(string)));
+ }
+
+ [Test(Description = "Should be able to inject and inject values for lists of primitive elements, even with the lists currently set to null")]
+ public void Should_Bind_Primitive_Lists_With_Instantiated_Target_Objects()
+ {
+ var simpleListClass = new SimpleListClass {dates = new List<DateTime>(), strings = new List<string>()};
+
+ _matcher.Match(simpleListClass);
+
+ Assert.IsNotNull(simpleListClass.dates);
+ Assert.IsNotNull(simpleListClass.strings);
+ Assert.IsTrue(simpleListClass.dates.Count > 0);
+ Assert.IsTrue(simpleListClass.strings.Count > 0);
+ Assert.IsTrue(simpleListClass.dates.All(x => x != default(DateTime)));
+ Assert.IsTrue(simpleListClass.strings.All(x => x != default(string)));
+ }
+
+ [Test(Description = "Should be able to inject and map values for lists that contain other POCO objects")]
+ public void Should_Bind_Lists_With_Poco_Objects()
+ {
+ var richListClass = new PocoListsClass();
+
+ _matcher.Match(richListClass);
+
+ Assert.IsNotNull(richListClass.Users);
+ Assert.IsTrue(richListClass.Users.Count > 0);
+
+ foreach(var user in richListClass.Users)
+ {
+ /* Assert to see that we have populated all of the fields on our test instance */
+ Assert.AreNotEqual(user.UserID, default(int));
+ Assert.AreNotEqual(user.Timestamp, default(long));
+ Assert.AreNotEqual(user.DateRegistered, default(DateTime));
+
+ Assert.IsNotNullOrEmpty(user.Name);
+ Assert.IsNotNullOrEmpty(user.Email);
+ Assert.IsTrue(_valid_email_regex.IsMatch(user.Email));
+ }
+ }
+
+ #endregion
+ }
+}
View
28 Faker/Fake.cs
@@ -12,11 +12,14 @@ namespace Faker
/// </summary>
public class Fake<T> where T : new()
{
- private IList<ITypeSelector> _selectors;
+ /// <summary>
+ /// Engine used to power our fakes
+ /// </summary>
+ private readonly Matcher _matcher;
public Fake()
{
- _selectors = new List<ITypeSelector>();
+ _matcher = new Matcher();
}
/// <summary>
@@ -25,7 +28,14 @@ public Fake()
/// <returns>A populated instance of a given class</returns>
T Generate()
{
- throw new NotImplementedException();
+ //create a new instance of the type we want to Fake
+ var instance = (T)Matcher.SafeObjectCreate(typeof(T));
+
+ //Match all of the properties of the object and come up with the most reasonable guess we can as to the type of data needed
+ _matcher.Match(instance);
+
+ //Return the instance once matching is complete
+ return instance;
}
/// <summary>
@@ -34,7 +44,17 @@ T Generate()
/// <returns>A list of populated instances with length [count] of a given class</returns>
IList<T> Generate(int count)
{
- throw new NotImplementedException();
+ //Create a list to hold all of the fakes we want to return back to the caller
+ var items = new List<T>();
+
+ //Build the list of objects
+ for(var i = 0; i < count; i++)
+ {
+ items.Add(this.Generate());
+ }
+
+ //Return the list to the caller
+ return items;
}
}
}
View
78 Faker/Matcher.cs
@@ -20,7 +20,7 @@ public class Matcher
/// <summary>
/// Default constructor - uses the default TypeTable
/// </summary>
- public Matcher():this(new TypeTable()){}
+ public Matcher() : this(new TypeTable()) { }
/// <summary>
/// Constructor which accepts a TypeTable as an argument
@@ -39,7 +39,7 @@ public Matcher(TypeTable table)
public virtual void Match<T>(T targetObject) where T : new()
{
//Get all of the properties of the class
- var properties = typeof (T).GetProperties();
+ var properties = typeof(T).GetProperties();
ProcessProperties(properties, targetObject);
}
@@ -75,21 +75,21 @@ protected virtual void ProcessProperty(PropertyInfo property, object targetObjec
var selectorCount = TypeMap.CountSelectors(propertyType);
//We have some matching selectors, so we'll evaluate and return the best match
- if(selectorCount > 0)
+ if (selectorCount > 0)
{
//Evaluate all of the possible selectors and find the first available match
var selector = EvaluateSelectors(property, TypeMap.GetSelectors(propertyType));
//We found a matching selector
- if(!(selector is MissingSelector))
+ if (!(selector is MissingSelector))
{
selector.Generate(targetObject, property); //Bind the property
return; //Exit
}
}
//Check to see if the type is a class and has a default constructor
- if (propertyType.IsClass && propertyType.GetConstructor(Type.EmptyTypes) != null)
+ if (propertyType.IsClass && propertyType.GetConstructor(Type.EmptyTypes) != null && !IsArray(propertyType))
{
var subProperties = propertyType.GetProperties();
@@ -106,60 +106,90 @@ protected virtual void ProcessProperty(PropertyInfo property, object targetObjec
}
//Check to see if the type is an array or any other sort of collection
- if(typeof(IList).IsAssignableFrom(propertyType))
+ if (IsArray(propertyType))
{
//Get the underlying type used int he array
- var elementType = propertyType.GetElementType();
+ //var elementType = propertyType.GetElementType(); //Works only for arrays
+ var elementType = propertyType.GetGenericArguments()[0]; //Works for IList<T> / IEnumerable<T>
//Get a number of elements we want to create
- //Note: (between 0 and 10 for now)
- var elementCount = Numbers.Int(0, 10);
-
+ //Note: (between 1 and 10 for now)
+ var elementCount = Numbers.Int(1, 10);
+
//Create an instance of our target array
IList arrayInstance = null;
//If we're working with a generic list or any other sort of collection
- if(propertyType.IsGenericType)
+ if (propertyType.IsGenericTypeDefinition)
{
arrayInstance = (IList)GenericHelper.CreateGeneric(propertyType, elementType);
}
else
{
- arrayInstance = (IList) GenericHelper.CreateGeneric(typeof (List<>), elementType);
+ arrayInstance = (IList)GenericHelper.CreateGeneric(typeof(List<>), elementType);
}
//Determine if there's a selector available for this type
var hasSelector = TypeMap.CountSelectors(elementType) > 0;
ITypeSelector selector = null;
- if(hasSelector)
+ //So we have a type available for this selector..
+ if (hasSelector)
{
- //selector = EvaluateSelectors(ele)
+ selector = TypeMap.GetBaseSelector(elementType);
}
- for(var i =0; i < elementCount; i++)
+ for (var i = 0; i < elementCount; i++)
{
//Create a new element instance
- var element = Activator.CreateInstance(elementType);
+ var element = SafeObjectCreate(elementType);
- if(hasSelector)
+ if (hasSelector)
{
-
+ selector.Generate(ref element);
}
-
- //If the element type is a sub-class, then populate it recursively
- if(elementType.IsClass)
+ else if (elementType.IsClass) //If the element type is a sub-class, then populate it recursively
{
- var subProperties = propertyType.GetProperties();
+ var subProperties = elementType.GetProperties();
//Populate all of the properties on this object
ProcessProperties(subProperties, element);
}
arrayInstance.Add(element);
}
+
+ //Bind the sub-class back onto the original target object
+ property.SetValue(targetObject, arrayInstance, null);
+ }
+
+ }
+
+ /// <summary>
+ /// Returns true if the targeted type is an array of some sort
+ /// </summary>
+ /// <param name="targetType">the type we want to test</param>
+ /// <returns>true if it's an array, false otherwise</returns>
+ protected virtual bool IsArray(Type targetType)
+ {
+ return typeof(IList).IsAssignableFrom(targetType);
+ }
+
+ /// <summary>
+ /// Method used for safely creating new instances of type objects; handles a few special cases
+ /// where activation has to be done carefully.
+ /// </summary>
+ /// <param name="t">The target type we want to instantiate</param>
+ /// <returns>an instance of the specified type</returns>
+ public static object SafeObjectCreate(Type t)
+ {
+ //If the object is a string (tricky)
+ if (t == typeof(string))
+ {
+ return string.Empty;
}
+ return Activator.CreateInstance(t);
}
/// <summary>
@@ -170,10 +200,10 @@ protected virtual void ProcessProperty(PropertyInfo property, object targetObjec
/// <returns>the first matching ITypeSelector instance we could find</returns>
protected virtual ITypeSelector EvaluateSelectors(PropertyInfo propertyType, IEnumerable<ITypeSelector> selectors)
{
- foreach(var selector in selectors)
+ foreach (var selector in selectors)
{
//If the selector can bind
- if(selector.CanBind(propertyType))
+ if (selector.CanBind(propertyType))
{
//Return it
return selector;

0 comments on commit a889791

Please sign in to comment.