Skip to content

Commit dc98ac6

Browse files
committed
fix: abstract/interface may have different underlying type
1 parent 405651a commit dc98ac6

2 files changed

Lines changed: 37 additions & 14 deletions

File tree

Directory.Build.props

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
</PropertyGroup>
66

77
<PropertyGroup Label="NuGetPublishSettings">
8-
<NuGetPublishVersion>0.8.1</NuGetPublishVersion>
8+
<NuGetPublishVersion>0.8.2</NuGetPublishVersion>
99
<NuGetPublishVersionSuffix></NuGetPublishVersionSuffix>
1010
<!-- relative path from .csproj files, without trailing slash -->
1111
<NuGetPublishProjectRootRelPath>..</NuGetPublishProjectRootRelPath>

src/Must.Reflection.cs

Lines changed: 36 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,44 +12,51 @@ partial class Must
1212
/// <summary>
1313
/// Asserts that two objects have equal properties, performing a deep comparison.
1414
/// </summary>
15-
/// <typeparam name="T">The type of the objects to compare.</typeparam>
1615
/// <param name="expected">The expected object.</param>
1716
/// <param name="actual">The actual object.</param>
1817
/// <param name="propertyNamesToSkip">Optional. An array of property names to skip during the comparison.</param>
1918
/// <param name="logger">Optional. A logger action to output comparison details.</param>
20-
public static void HaveEqualProperties<T>(
21-
T expected,
22-
T actual,
19+
public static void HaveEqualProperties<TExpected, TActual>(
20+
TExpected expected,
21+
TActual actual,
2322
string[]? propertyNamesToSkip = null,
2423
Action<string>? logger = null
2524
)
25+
where TActual : TExpected
2626
{
27-
DeepEquals(expected, actual, depth: 0, propertyOrFieldPath: "$", compareByProperty: true, propertyNamesToSkip, logger);
27+
DeepEquals(
28+
expected, actual, depth: 0, typeof(TExpected).IsAbstract || typeof(TActual).IsAbstract,
29+
propertyOrFieldPath: "$", compareByProperty: true, propertyNamesToSkip, logger
30+
);
2831
}
2932

3033
/// <summary>
3134
/// Asserts that two objects have equal fields, performing a deep comparison.
3235
/// </summary>
33-
/// <typeparam name="T">The type of the objects to compare.</typeparam>
3436
/// <param name="expected">The expected object.</param>
3537
/// <param name="actual">The actual object.</param>
3638
/// <param name="fieldNamesToSkip">Optional. An array of field names to skip during the comparison.</param>
3739
/// <param name="logger">Optional. A logger action to output comparison details.</param>
38-
public static void HaveEqualFields<T>(
39-
T expected,
40-
T actual,
40+
public static void HaveEqualFields<TExpected, TActual>(
41+
TExpected expected,
42+
TActual actual,
4143
string[]? fieldNamesToSkip = null,
4244
Action<string>? logger = null
4345
)
46+
where TActual : TExpected
4447
{
45-
DeepEquals(expected, actual, depth: 0, propertyOrFieldPath: "$", compareByProperty: false, fieldNamesToSkip, logger);
48+
DeepEquals(
49+
expected, actual, depth: 0, typeof(TExpected).IsAbstract || typeof(TActual).IsAbstract,
50+
propertyOrFieldPath: "$", compareByProperty: false, fieldNamesToSkip, logger
51+
);
4652
}
4753

4854

4955
static void DeepEquals<T>(
5056
T expected,
5157
T actual,
5258
int depth,
59+
bool isAbstractType,
5360
string propertyOrFieldPath,
5461
bool compareByProperty,
5562
string[]? propertyOrFieldNamesToSkip,
@@ -126,7 +133,10 @@ expected is string ||
126133
expectedMap.Remove(key, out var expectedValue);
127134
actualMap.Remove(key, out var actualValue);
128135

129-
DeepEquals(expectedValue, actualValue, depth, $"{propertyOrFieldPath}[{key}]", compareByProperty, propertyOrFieldNamesToSkip, logger);
136+
DeepEquals(expectedValue, actualValue, depth,
137+
expectedValue?.GetType().IsAbstract == true || actualValue?.GetType().IsAbstract == true,
138+
$"{propertyOrFieldPath}[{key}]", compareByProperty, propertyOrFieldNamesToSkip, logger
139+
);
130140
}
131141
}
132142
else if (
@@ -151,7 +161,10 @@ expected is not string and IEnumerable expectedEnumerable &&
151161

152162
for (int i = 0, count = Math.Min(expectedList.Count, actualList.Count); i < count; i++)
153163
{
154-
DeepEquals(expectedList[i], actualList[i], depth, $"{propertyOrFieldPath}[{i}]", compareByProperty, propertyOrFieldNamesToSkip, logger);
164+
DeepEquals(expectedList[i], actualList[i], depth,
165+
expectedList[i]?.GetType().IsAbstract == true || actualList[i]?.GetType().IsAbstract == true,
166+
$"{propertyOrFieldPath}[{i}]", compareByProperty, propertyOrFieldNamesToSkip, logger
167+
);
155168
}
156169
}
157170

@@ -198,6 +211,11 @@ expected is not string and IEnumerable expectedEnumerable &&
198211
E = compareByProperty ? ((PropertyInfo)member).GetValue(expected) : ((FieldInfo)member).GetValue(expected);
199212
A = compareByProperty ? ((PropertyInfo)member).GetValue(actual) : ((FieldInfo)member).GetValue(actual);
200213
}
214+
catch (TargetException) when (isAbstractType)
215+
{
216+
logger?.Invoke($"{indent}[SKIP] Actual type of abstractions are different: {member.Name} ({typedef})");
217+
continue;
218+
}
201219
catch (Exception error)
202220
{
203221
throw new FUnitException($"{member.Name} ({typedef}): {error.Message}", error);
@@ -221,7 +239,12 @@ expected is not string and IEnumerable expectedEnumerable &&
221239
var memberFullPath = $"{propertyOrFieldPath}.{member.Name}";
222240

223241
// main
224-
DeepEquals(E, A, depth, memberFullPath, compareByProperty, propertyOrFieldNamesToSkip, logger);
242+
DeepEquals(
243+
E, A, depth,
244+
E?.GetType().IsAbstract == true || A?.GetType().IsAbstract == true,
245+
memberFullPath, compareByProperty, propertyOrFieldNamesToSkip, logger
246+
);
247+
225248
continue;
226249

227250
throw new FUnitException($"cannot compare object: expected: '{E}', actual: '{A}'");

0 commit comments

Comments
 (0)