Last active
August 29, 2015 14:01
-
-
Save quexy/66dff399be40bdbe7057 to your computer and use it in GitHub Desktop.
Collection verifier for SpecFlow
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Linq.Expressions; | |
namespace TechTalk.SpecFlow.ObjectVerification | |
{ | |
public static class CollectionVerifierExtensions | |
{ | |
/// <summary> | |
/// Specifies the aliases in the verification data for the given property of the object. There can be more than one alias for any property, but not vice versa. | |
/// </summary> | |
public static IConfiguredCollectionVerifier<TObject> WithPropertyAlias<TObject, TProperty>(this IConfiguredCollectionVerifier<TObject> collectionVerifier, Expression<Func<TObject, TProperty>> selector, params string[] aliases) | |
{ | |
foreach (var alias in aliases) | |
collectionVerifier.WithPropertyAlias(selector, alias); | |
return collectionVerifier; | |
} | |
/// <summary> | |
/// Specifies the equality comparer to use when verifying the given field mapped to the verified object (comparison priority: table field, object property, target value, default) | |
/// </summary> | |
public static IConfiguredCollectionVerifier<TObject> WithFieldEqualityComparer<TObject, TValue>(this IConfiguredCollectionVerifier<TObject> collectionVerifier, string name, IEqualityComparer<TValue> comparer) | |
{ | |
collectionVerifier.WithFieldEqualityComparer<TValue>(name, (a, b) => comparer.Equals(a, b)); | |
return collectionVerifier; | |
} | |
/// <summary> | |
/// Specifies the equality comparer to use when verifying the given property of the verified object (comparison priority: table field, object property, target value, default) | |
/// </summary> | |
public static IConfiguredCollectionVerifier<TObject> WithPropertyEqualityComparer<TObject, TValue>(this IConfiguredCollectionVerifier<TObject> collectionVerifier, Expression<Func<TObject, TValue>> selector, IEqualityComparer<TValue> comparer) | |
{ | |
collectionVerifier.WithPropertyEqualityComparer<TValue>(selector, (a, b) => comparer.Equals(a, b)); | |
return collectionVerifier; | |
} | |
/// <summary> | |
/// Specifies the equality comparer to use when verifying properties of the given type (comparison priority: table field, object property, target value, default) | |
/// </summary> | |
public static IConfiguredCollectionVerifier<TObject> WithTypeEqualityComparer<TObject, TValue>(this IConfiguredCollectionVerifier<TObject> collectionVerifier, IEqualityComparer<TValue> comparer) | |
{ | |
collectionVerifier.WithTypeEqualityComparer<TValue>((a, b) => comparer.Equals(a, b)); | |
return collectionVerifier; | |
} | |
} | |
public interface ICollectionVerifier<TObject> | |
{ | |
/// <summary> | |
/// Allows customizing the collection object verification | |
/// </summary> | |
IConfiguredCollectionVerifier<TObject> WithConfiguration(); | |
/// <summary> | |
/// Verifies whether the two collections are equal, that is their object counts are the same, and the object at each position are matching | |
/// </summary> | |
string[] AreEqual(IEnumerable<IDictionary<string, string>> expectedCollection, IEnumerable<TObject> actualCollection); | |
/// <summary> | |
/// Verifies whether the two collections are equivalent, that is their object counts are the same, and each object has a pair regardless of their positions | |
/// </summary> | |
string[] AreEquivalent(IEnumerable<IDictionary<string, string>> expectedCollection, IEnumerable<TObject> actualCollection); | |
/// <summary> | |
/// Verifies whether every element in the subcollection has a pair in the main collection | |
/// </summary> | |
string[] IsSubset(IEnumerable<IDictionary<string, string>> subcollection, IEnumerable<TObject> mainCollection); | |
/// <summary> | |
/// Verifies whether every element in the subcollection has a pair in the main collection | |
/// </summary> | |
string[] IsSubset(IEnumerable<TObject> subcollection, IEnumerable<IDictionary<string, string>> mainCollection); | |
} | |
public interface IConfiguredCollectionVerifier<TObject> : ICollectionVerifier<TObject> | |
{ | |
/// <summary> | |
/// Specifies an alias in the verification data for the given property of the object. There can be more than one alias for any property, but not vice versa. | |
/// </summary> | |
IConfiguredCollectionVerifier<TObject> WithPropertyAlias<TProperty>(Expression<Func<TObject, TProperty>> selector, string alias); | |
/// <summary> | |
/// Specifies the converter to use for the given verification data field (converter priority: table field, object property, target value, default) | |
/// </summary> | |
IConfiguredCollectionVerifier<TObject> WithFieldValueConverter<TProperty>(string name, Func<string, TProperty> converter); | |
/// <summary> | |
/// Specifies the converter to use for the given property of the verified object (converter priority: table field, object property, target value, default) | |
/// </summary> | |
IConfiguredCollectionVerifier<TObject> WithPropertyValueConverter<TProperty>(Expression<Func<TObject, TProperty>> selector, Func<string, TProperty> converter); | |
/// <summary> | |
/// Specifies a conversion method to use when converting to the given value (converter priority: table field, object property, target value, default) | |
/// </summary> | |
IConfiguredCollectionVerifier<TObject> WithTypeValueConverter<TValue>(Func<string, TValue> converter); | |
/// <summary> | |
/// Specifies the provider method to use to obtain the default value converter for any given type (converter priority: table field, object property, target value, default) | |
/// </summary> | |
IConfiguredCollectionVerifier<TObject> WithDefaultConverter(Func<Type, Func<string, object>> converterProvider); | |
/// <summary> | |
/// Specifies the equality comparison method to use when verifying the given field mapped to the verified object (comparison priority: table field, object property, target value, default) | |
/// </summary> | |
IConfiguredCollectionVerifier<TObject> WithFieldEqualityComparer<TValue>(string name, Func<TValue, TValue, bool> comparer); | |
/// <summary> | |
/// Specifies the equality comparison method to use when verifying the given property of the verified object (comparison priority: table field, object property, target value, default) | |
/// </summary> | |
IConfiguredCollectionVerifier<TObject> WithPropertyEqualityComparer<TValue>(Expression<Func<TObject, TValue>> selector, Func<TValue, TValue, bool> comparer); | |
/// <summary> | |
/// Specifies the equality comparison method to use when verifying properties of the given type (comparison priority: table field, object property, target value, default) | |
/// </summary> | |
IConfiguredCollectionVerifier<TObject> WithTypeEqualityComparer<TValue>(Func<TValue, TValue, bool> comparer); | |
} | |
public static class CollectionVerifier | |
{ | |
/// <summary> | |
/// Creates a collection verifier for collections of the given type | |
/// </summary> | |
public static ICollectionVerifier<TObject> For<TObject>() | |
{ | |
return new CollectionVerifier<TObject>(); | |
} | |
} | |
internal sealed class CollectionVerifier<TObject> : IConfiguredCollectionVerifier<TObject> | |
{ | |
private static string keyField = "key"; | |
private static string valueField = "value"; | |
private readonly IObjectVerifier<TObject> objectVerifier; | |
public CollectionVerifier() | |
{ | |
objectVerifier = ObjectVerifier.For<TObject>(keyField, valueField); | |
} | |
private IEnumerable<IDictionary<string, string>> ConvertExpectedObject(IDictionary<string, string> expectedObject) | |
{ | |
return expectedObject.Select(e => new Dictionary<string, string> { { keyField, e.Key }, { valueField, e.Value } }); | |
} | |
public string[] AreEqual(IEnumerable<IDictionary<string, string>> expectedCollection, IEnumerable<TObject> actualCollection) | |
{ | |
var expectedArray = expectedCollection.ToArray(); | |
var actualArray = actualCollection.ToArray(); | |
var expectedCount = expectedArray.Length; | |
var actualCount = actualArray.Length; | |
if (expectedCount != actualCount) | |
return new[] { string.Format("The collection sizes are not equal (expected count: <{0}>; actual count: <{1}>)", expectedCount, actualCount) }; | |
var errors = new List<string>(); | |
for (int index = 0; index < expectedCount; ++index) | |
{ | |
var verificationResult = objectVerifier.Verify(ConvertExpectedObject(expectedArray[index]), actualArray[index]); | |
if (verificationResult.Length != 0) | |
errors.Add(string.Format("Objects at index {0} do not match: {1}", index, string.Join(",", verificationResult))); | |
} | |
return errors.ToArray(); | |
} | |
public string[] AreEquivalent(IEnumerable<IDictionary<string, string>> expectedCollection, IEnumerable<TObject> actualCollection) | |
{ | |
var expectedArray = expectedCollection.ToArray(); | |
var actualArray = actualCollection.ToArray(); | |
var expectedCount = expectedArray.Length; | |
var actualCount = actualArray.Length; | |
if (expectedCount != actualCount) | |
return new[] { string.Format("The collection sizes are not equal (expected count: <{0}>; actual count: <{1}>)", expectedCount, actualCount) }; | |
var errors = new List<string>(); | |
foreach (var expectedObject in expectedArray) | |
{ | |
var expected = ConvertExpectedObject(expectedObject); | |
if (!actualArray.Any(actual => objectVerifier.Verify(expected, actual).Length == 0)) | |
errors.Add(string.Format("No matching object for {0}{2}{1}", "{", "}", | |
string.Join(",", expectedObject.Select(e => string.Format("{0}:\"{1}\"", e.Key, e.Value))))); | |
} | |
return errors.ToArray(); | |
} | |
public string[] IsSubset(IEnumerable<IDictionary<string, string>> subcollection, IEnumerable<TObject> mainCollection) | |
{ | |
var expectedArray = subcollection.ToArray(); | |
var actualArray = mainCollection.ToArray(); | |
var expectedCount = expectedArray.Length; | |
var actualCount = actualArray.Length; | |
if (expectedCount > actualCount) | |
return new[] { string.Format("The subcollection is bigger than the main collection (expected count: <{0}>; actual count: <{1}>)", expectedCount, actualCount) }; | |
var errors = new List<string>(); | |
foreach (var expectedObject in expectedArray) | |
{ | |
var expected = ConvertExpectedObject(expectedObject); | |
if (!actualArray.Any(actual => objectVerifier.Verify(expected, actual).Length == 0)) | |
errors.Add(string.Format("No matching object for {0}{2}{1}", "{", "}", | |
string.Join(",", expectedObject.Select(e => string.Format("{0}:\"{1}\"", e.Key, e.Value))))); | |
} | |
return errors.ToArray(); | |
} | |
public string[] IsSubset(IEnumerable<TObject> subcollection, IEnumerable<IDictionary<string, string>> mainCollection) | |
{ | |
var expectedArray = subcollection.ToArray(); | |
var actualArray = mainCollection.Select(ConvertExpectedObject).ToArray(); | |
var expectedCount = expectedArray.Length; | |
var actualCount = actualArray.Length; | |
if (expectedCount > actualCount) | |
return new[] { string.Format("The subcollection is bigger than the main collection (expected count: <{0}>; actual count: <{1}>)", expectedCount, actualCount) }; | |
var errors = new List<string>(); | |
foreach (var expected in expectedArray) | |
{ | |
if (!actualArray.Any(actual => objectVerifier.Verify(actual, expected).Length == 0)) | |
errors.Add(string.Format("No matching object for {0}{2}{1}", "{", "}", | |
string.Join(",", typeof(TObject).GetProperties().Select(p => string.Format("{0}:\"{1}\"", p.Name, p.GetValue(expected)))))); | |
} | |
return errors.ToArray(); | |
} | |
public IConfiguredCollectionVerifier<TObject> WithConfiguration() | |
{ | |
return this; | |
} | |
public IConfiguredCollectionVerifier<TObject> WithPropertyAlias<TProperty>(Expression<Func<TObject, TProperty>> selector, string alias) | |
{ | |
objectVerifier.WithConfiguration().WithPropertyAlias(selector, alias); | |
return this; | |
} | |
public IConfiguredCollectionVerifier<TObject> WithFieldValueConverter<TProperty>(string name, Func<string, TProperty> converter) | |
{ | |
objectVerifier.WithConfiguration().WithFieldValueConverter(name, converter); | |
return this; | |
} | |
public IConfiguredCollectionVerifier<TObject> WithPropertyValueConverter<TProperty>(Expression<Func<TObject, TProperty>> selector, Func<string, TProperty> converter) | |
{ | |
objectVerifier.WithConfiguration().WithPropertyValueConverter(selector, converter); | |
return this; | |
} | |
public IConfiguredCollectionVerifier<TObject> WithTypeValueConverter<TValue>(Func<string, TValue> converter) | |
{ | |
objectVerifier.WithConfiguration().WithTypeValueConverter(converter); | |
return this; | |
} | |
public IConfiguredCollectionVerifier<TObject> WithDefaultConverter(Func<Type, Func<string, object>> converterProvider) | |
{ | |
objectVerifier.WithConfiguration().WithDefaultConverter(converterProvider); | |
return this; | |
} | |
public IConfiguredCollectionVerifier<TObject> WithFieldEqualityComparer<TValue>(string name, Func<TValue, TValue, bool> comparer) | |
{ | |
objectVerifier.WithConfiguration().WithFieldEqualityComparer(name, comparer); | |
return this; | |
} | |
public IConfiguredCollectionVerifier<TObject> WithPropertyEqualityComparer<TValue>(Expression<Func<TObject, TValue>> selector, Func<TValue, TValue, bool> comparer) | |
{ | |
objectVerifier.WithConfiguration().WithPropertyEqualityComparer(selector, comparer); | |
return this; | |
} | |
public IConfiguredCollectionVerifier<TObject> WithTypeEqualityComparer<TValue>(Func<TValue, TValue, bool> comparer) | |
{ | |
objectVerifier.WithConfiguration().WithTypeEqualityComparer(comparer); | |
return this; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment