@@ -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