Skip to content

Commit

Permalink
Preliminary work on adding a table helpers class that can be resolved…
Browse files Browse the repository at this point in the history
… from the DI. This enables per-scenario differing value retrievers.
  • Loading branch information
ajeckmans committed Apr 20, 2024
1 parent c9b362f commit 7ca1d75
Show file tree
Hide file tree
Showing 53 changed files with 1,310 additions and 236 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ namespace TechTalk.SpecFlow.Assist;
public static class InstanceComparisonExtensionMethods
{

[Obsolete("Use TableHelpers instead")]
public static void CompareToInstance<T>(this Table table, T instance)
{
Reqnroll.InstanceComparisonExtensionMethods.CompareToInstance(table, instance);
Expand All @@ -16,6 +17,7 @@ public static void CompareToInstance<T>(this Table table, T instance)
/// Indicates whether the table is equivalent to the specified instance by comparing the values of all
/// columns against the properties of the instance. Will return false after finding the first difference.
/// </summary>
[Obsolete("Use TableHelpers instead")]
public static bool IsEquivalentToInstance<T>(this Table table, T instance)
{
return Reqnroll.InstanceComparisonExtensionMethods.IsEquivalentToInstance(table, instance);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
using System;
using System.Collections.Generic;
using Reqnroll;

// ReSharper disable once CheckNamespace
namespace TechTalk.SpecFlow.Assist;

public static class SetComparisonExtensionMethods
{
[Obsolete("Use TableHelpers instead")]
public static void CompareToSet<T>(this Table table, IEnumerable<T> set, bool sequentialEquality = false)
{
Reqnroll.SetComparisonExtensionMethods.CompareToSet(table, set, sequentialEquality);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,63 +8,75 @@ namespace TechTalk.SpecFlow.Assist;

public static class TableHelperExtensionMethods
{
[Obsolete("Use TableHelpers instead")]
public static T CreateInstance<T>(this Table table)
{
return Reqnroll.TableHelperExtensionMethods.CreateInstance<T>(table);
return Reqnroll.TableExtensionMethods.CreateInstance<T>(table);
}

[Obsolete("Use TableHelpers instead")]
public static T CreateInstance<T>(this Table table, InstanceCreationOptions creationOptions)
{
return Reqnroll.TableHelperExtensionMethods.CreateInstance<T>(table, creationOptions);
return Reqnroll.TableExtensionMethods.CreateInstance<T>(table, creationOptions);
}

[Obsolete("Use TableHelpers instead")]
public static T CreateInstance<T>(this Table table, Func<T> methodToCreateTheInstance)
{
return Reqnroll.TableHelperExtensionMethods.CreateInstance(table, methodToCreateTheInstance);
return Reqnroll.TableExtensionMethods.CreateInstance(table, methodToCreateTheInstance);
}

[Obsolete("Use TableHelpers instead")]
public static T CreateInstance<T>(this Table table, Func<T> methodToCreateTheInstance, InstanceCreationOptions creationOptions)
{
return Reqnroll.TableHelperExtensionMethods.CreateInstance(table, methodToCreateTheInstance, creationOptions);
return Reqnroll.TableExtensionMethods.CreateInstance(table, methodToCreateTheInstance, creationOptions);
}

[Obsolete("Use TableHelpers instead")]
public static void FillInstance(this Table table, object instance)
{
Reqnroll.TableHelperExtensionMethods.FillInstance(table, instance);
Reqnroll.TableExtensionMethods.FillInstance(table, instance);
}

[Obsolete("Use TableHelpers instead")]
public static void FillInstance(this Table table, object instance, InstanceCreationOptions creationOptions)
{
Reqnroll.TableHelperExtensionMethods.FillInstance(table, instance, creationOptions);
Reqnroll.TableExtensionMethods.FillInstance(table, instance, creationOptions);
}

[Obsolete("Use TableHelpers instead")]
public static IEnumerable<T> CreateSet<T>(this Table table)
{
return Reqnroll.TableHelperExtensionMethods.CreateSet<T>(table);
return Reqnroll.TableExtensionMethods.CreateSet<T>(table);
}

[Obsolete("Use TableHelpers instead")]
public static IEnumerable<T> CreateSet<T>(this Table table, InstanceCreationOptions creationOptions)
{
return Reqnroll.TableHelperExtensionMethods.CreateSet<T>(table, creationOptions);
return Reqnroll.TableExtensionMethods.CreateSet<T>(table, creationOptions);
}

[Obsolete("Use TableHelpers instead")]
public static IEnumerable<T> CreateSet<T>(this Table table, Func<T> methodToCreateEachInstance)
{
return Reqnroll.TableHelperExtensionMethods.CreateSet<T>(table, methodToCreateEachInstance);
return Reqnroll.TableExtensionMethods.CreateSet<T>(table, methodToCreateEachInstance);
}

[Obsolete("Use TableHelpers instead")]
public static IEnumerable<T> CreateSet<T>(this Table table, Func<T> methodToCreateEachInstance, InstanceCreationOptions creationOptions)
{
return Reqnroll.TableHelperExtensionMethods.CreateSet<T>(table, methodToCreateEachInstance, creationOptions);
return Reqnroll.TableExtensionMethods.CreateSet<T>(table, methodToCreateEachInstance, creationOptions);
}

[Obsolete("Use TableHelpers instead")]
public static IEnumerable<T> CreateSet<T>(this Table table, Func<DataTableRow, T> methodToCreateEachInstance)
{
return Reqnroll.TableHelperExtensionMethods.CreateSet<T>(table, methodToCreateEachInstance);
return Reqnroll.TableExtensionMethods.CreateSet<T>(table, methodToCreateEachInstance);
}

[Obsolete("Use TableHelpers instead")]
public static IEnumerable<T> CreateSet<T>(this Table table, Func<DataTableRow, T> methodToCreateEachInstance, InstanceCreationOptions creationOptions)
{
return Reqnroll.TableHelperExtensionMethods.CreateSet<T>(table, methodToCreateEachInstance, creationOptions);
return Reqnroll.TableExtensionMethods.CreateSet<T>(table, methodToCreateEachInstance, creationOptions);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,7 @@ public void CreateEmptyCSharpCore3_1ProjectInNewFormat()
string projectFileContent = GetProjectFileContent(solutionFolder, project);

projectFileContent.Should()
.Contain("<Project Sdk=\"Microsoft.NET.Sdk\">\r\n <PropertyGroup>\r\n <TargetFramework>netcoreapp3.1</TargetFramework>\r\n </PropertyGroup>\r\n</Project>");
.Contain("<Project Sdk=\"Microsoft.NET.Sdk\">\r\n <PropertyGroup>\r\n <TargetFramework>net6.0</TargetFramework>\r\n </PropertyGroup>\r\n</Project>");
}

[Fact]
Expand Down
4 changes: 2 additions & 2 deletions Reqnroll/Assist/EnumerableProjection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,14 @@ public class EnumerableProjection<T> : IEnumerable<Projection<T>>
private readonly IEnumerable<T> collection;
private readonly IEnumerable<string> properties;

public EnumerableProjection(Table table, IEnumerable<T> collection = null)
public EnumerableProjection(TableHelpers tableHelpers, Table table, IEnumerable<T> collection = null)
{
if (table == null && collection == null)
throw new ArgumentNullException(nameof(table), "Either table or projectCollection must be specified");

if (table != null)
properties = table.Header;
this.collection = collection ?? table.CreateSet<T>();
this.collection = collection ?? tableHelpers.CreateSet<T>(table);
}

public IEnumerator<Projection<T>> GetEnumerator()
Expand Down
20 changes: 11 additions & 9 deletions Reqnroll/Assist/FindInSetExtensionMethods.cs
Original file line number Diff line number Diff line change
@@ -1,32 +1,34 @@
using System;
using System.Collections.Generic;
using System.Linq;

namespace Reqnroll.Assist
{
public static class FindInSetExtensionMethods
{
[Obsolete("Use TableHelpers instead")]
public static T FindInSet<T>(this Table table, IEnumerable<T> set)
{
var instanceTable = TEHelpers.GetTheProperInstanceTable(table, typeof (T));
var instanceTable = TEHelpers.GetTheProperInstanceTable(table, typeof(T));

var matches = set.Where(instance => InstanceMatchesTable(instance, instanceTable)).ToArray();
var matches = set.Where(instance => InstanceMatchesTable(Service.Instance, instance, instanceTable)).ToArray();

if (matches.Length > 1)
throw new ComparisonException("Multiple instances match the table");
if (matches.Length > 1) throw new ComparisonException("Multiple instances match the table");

return matches.FirstOrDefault();
}

[Obsolete("Use TableHelpers instead")]
public static IEnumerable<T> FindAllInSet<T>(this Table table, IEnumerable<T> set)
{
var instanceTable = TEHelpers.GetTheProperInstanceTable(table, typeof (T));
var instanceTable = TEHelpers.GetTheProperInstanceTable(table, typeof(T));

return set.Where(instance => InstanceMatchesTable(instance, instanceTable)).ToArray();
return set.Where(instance => InstanceMatchesTable(Service.Instance, instance, instanceTable)).ToArray();
}

private static bool InstanceMatchesTable<T>(T instance, Table table)
private static bool InstanceMatchesTable<T>(Service service, T instance, Table table)
{
return table.Rows.All(row => !InstanceComparisonExtensionMethods.ThereIsADifference(instance, row));
return table.Rows.All(row => !InstanceComparisonExtensionMethods.ThereIsADifference(service, instance, row));
}
}
}
}
69 changes: 35 additions & 34 deletions Reqnroll/Assist/InstanceComparisonExtensionMethods.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,36 +8,35 @@ namespace Reqnroll
{
public static class InstanceComparisonExtensionMethods
{

[Obsolete("Use TableHelpers instead")]
public static void CompareToInstance<T>(this Table table, T instance)
{
AssertThatTheInstanceExists(instance);

var instanceTable = TEHelpers.GetTheProperInstanceTable(table, typeof(T));

var differences = FindAnyDifferences(instanceTable, instance);
var differences = FindAnyDifferences(Service.Instance, instanceTable, instance);

if (ThereAreAnyDifferences(differences))
ThrowAnExceptionThatDescribesThoseDifferences(differences);
if (ThereAreAnyDifferences(differences)) ThrowAnExceptionThatDescribesThoseDifferences(differences);
}

/// <summary>
/// Indicates whether the table is equivalent to the specified instance by comparing the values of all
/// columns against the properties of the instance. Will return false after finding the first difference.
/// </summary>
[Obsolete("Use TableHelpers instead")]
public static bool IsEquivalentToInstance<T>(this Table table, T instance)
{
AssertThatTheInstanceExists(instance);

var instanceTable = TEHelpers.GetTheProperInstanceTable(table, typeof(T));

return HasDifference(instanceTable, instance) == false;
return HasDifference(Service.Instance, instanceTable, instance) == false;
}

private static void AssertThatTheInstanceExists<T>(T instance)
{
if (instance == null)
throw new ComparisonException("The item to compare was null.");
if (instance == null) throw new ComparisonException("The item to compare was null.");
}

private static void ThrowAnExceptionThatDescribesThoseDifferences(IEnumerable<Difference> differences)
Expand All @@ -47,23 +46,25 @@ private static void ThrowAnExceptionThatDescribesThoseDifferences(IEnumerable<Di

private static string CreateDescriptiveErrorMessage(IEnumerable<Difference> differences)
{
return differences.Aggregate(@"The following fields did not match:",
(sum, next) => sum + (Environment.NewLine + next.Description));
return differences.Aggregate(
@"The following fields did not match:",
(sum, next) => sum + (Environment.NewLine + next.Description));
}

private static Difference[] FindAnyDifferences<T>(Table table, T instance)
private static Difference[] FindAnyDifferences<T>(Service service, Table table, T instance)
{
return (from row in table.Rows
where ThePropertyDoesNotExist(instance, row) || TheValuesDoNotMatch(instance, row)
select CreateDifferenceForThisRow(instance, row)).ToArray();
where ThePropertyDoesNotExist(instance, row) || TheValuesDoNotMatch(service, instance, row)
select CreateDifferenceForThisRow(service, instance, row)).ToArray();
}

private static bool HasDifference<T>(Table table, T instance)
private static bool HasDifference<T>(Service service, Table table, T instance)
{
// This method exists so it will stop evaluating the instance (hence stop using Reflection)
// after the first difference is found.
return (from row in table.Rows select row)
.Any(row => ThePropertyDoesNotExist(instance, row) || TheValuesDoNotMatch(instance, row));
return (from row in table.Rows
select row)
.Any(row => ThePropertyDoesNotExist(instance, row) || TheValuesDoNotMatch(service, instance, row));
}

private static bool ThereAreAnyDifferences(IEnumerable<Difference> differences)
Expand All @@ -73,50 +74,50 @@ private static bool ThereAreAnyDifferences(IEnumerable<Difference> differences)

internal static bool ThePropertyDoesNotExist<T>(T instance, DataTableRow row)
{
return instance.GetType().GetProperties()
.Any(property => TEHelpers.IsMemberMatchingToColumnName(property, row.Id())) == false;
return instance.GetType()
.GetProperties()
.Any(property => TEHelpers.IsMemberMatchingToColumnName(property, row.Id()))
== false;
}

internal static bool ThereIsADifference<T>(T instance, DataTableRow row)
internal static bool ThereIsADifference<T>(Service service, T instance, DataTableRow row)
{
return ThePropertyDoesNotExist(instance, row) || TheValuesDoNotMatch(instance, row);
return ThePropertyDoesNotExist(instance, row) || TheValuesDoNotMatch(service, instance, row);
}

private static bool TheValuesDoNotMatch<T>(T instance, DataTableRow row)
private static bool TheValuesDoNotMatch<T>(Service service, T instance, DataTableRow row)
{
var expected = GetTheExpectedValue(row);
var propertyValue = instance.GetPropertyValue(row.Id());
var comparer = FindValueComparerForValue(propertyValue);
var comparer = FindValueComparerForValue(service, propertyValue);

return comparer
.Compare(expected, propertyValue) == false;
.Compare(expected, propertyValue)
== false;
}

private static string GetTheExpectedValue(DataTableRow row)
{
return row.Value();
}

private static Difference CreateDifferenceForThisRow<T>(T instance, DataTableRow row)
private static Difference CreateDifferenceForThisRow<T>(Service service, T instance, DataTableRow row)
{
var propertyName = row.Id();

if (ThePropertyDoesNotExist(instance, row))
return new PropertyDoesNotExist(propertyName);
if (ThePropertyDoesNotExist(instance, row)) return new PropertyDoesNotExist(propertyName);

var expected = row.Value();
var actual = instance.GetPropertyValue(propertyName);
var comparer = FindValueComparerForProperty(instance, propertyName);
var comparer = FindValueComparerForProperty(service, instance, propertyName);
return new PropertyDiffers(propertyName, expected, actual, comparer);
}

private static IValueComparer FindValueComparerForProperty<T>(T instance, string propertyName) =>
FindValueComparerForValue(
instance.GetPropertyValue(propertyName));
private static IValueComparer FindValueComparerForProperty<T>(Service service, T instance, string propertyName) =>
FindValueComparerForValue(service, instance.GetPropertyValue(propertyName));

private static IValueComparer FindValueComparerForValue(object propertyValue) =>
Service.Instance.ValueComparers
.FirstOrDefault(x => x.CanCompare(propertyValue));
private static IValueComparer FindValueComparerForValue(Service service, object propertyValue) =>
service.ValueComparers.FirstOrDefault(x => x.CanCompare(propertyValue));

private abstract class Difference
{
Expand Down Expand Up @@ -156,7 +157,7 @@ public PropertyDiffers(string propertyName, object expected, object actual, IVal
}
}

public static class TableHelpers
public static class SetComparerTableHelpers
{
public static string Id(this DataTableRow row)
{
Expand All @@ -176,4 +177,4 @@ public ComparisonException(string message)
{
}
}
}
}
17 changes: 13 additions & 4 deletions Reqnroll/Assist/ProjectionExtensionMethods.cs
Original file line number Diff line number Diff line change
@@ -1,27 +1,36 @@
using System;
using System.Collections.Generic;

namespace Reqnroll.Assist
{
public static class ProjectionExtensionMethods
{
[Obsolete("Use TableHelpers instead")]
public static IEnumerable<Projection<T>> ToProjection<T>(this IEnumerable<T> collection, Table table = null)
{
return new EnumerableProjection<T>(table, collection);
var tableHelpers = new TableHelpers(Service.Instance);
return new EnumerableProjection<T>(tableHelpers, table, collection);
}

[Obsolete("Use TableHelpers instead")]
public static IEnumerable<Projection<T>> ToProjection<T>(this Table table)
{
return new EnumerableProjection<T>(table);
var tableHelpers = new TableHelpers(Service.Instance);
return new EnumerableProjection<T>(tableHelpers, table);
}

[Obsolete("Use TableHelpers instead")]
public static IEnumerable<Projection<T>> ToProjectionOfSet<T>(this Table table, IEnumerable<T> collection)
{
return new EnumerableProjection<T>(table);
var tableHelpers = new TableHelpers(Service.Instance);
return new EnumerableProjection<T>(tableHelpers, table);
}

[Obsolete("Use TableHelpers instead")]
public static IEnumerable<Projection<T>> ToProjectionOfInstance<T>(this Table table, T instance)
{
return new EnumerableProjection<T>(table);
var tableHelpers = new TableHelpers(Service.Instance);
return new EnumerableProjection<T>(tableHelpers, table);
}
}
}
Loading

0 comments on commit 7ca1d75

Please sign in to comment.