0

Ich bin in der Lage zu etwa zwei in Fluent Assertions wie diese 2D rechteckigen Arrays vergleichen:Fließend Assertions: Etwa zwei 2D rechteckigen Arrays vergleichen

float precision = 1e-5f; 
float[,] expectedArray = new float[,] { { 3.1f, 4.5f}, { 2, 4} }; 
float[,] calculatedArray = new float[,] { { 3.09f, 4.49f}, { 2, 4} }; 

for (int y = 0; y < 2; ++y) 
{ 
    for (int x = 0; x < 2; ++x) 
    { 
     calculatedArray[y,x].Should().BeApproximately(expectedArray[y,x], precision); 
    } 
} 

aber gibt es eine sauberere Möglichkeit, dies zu erreichen (ohne die für Schleifen) Zum Beispiel etwas in der gleichen Richtung wie diese (die für 1D-Arrays ist):

double[] source = { 10.01, 8.01, 6.01 }; 
double[] target = { 10.0, 8.0, 6.0 }; 

source.Should().Equal(target, (left, right) => Math.Abs(left-right) <= 0.01); 

Die obige Lösung für einen 1D-Array von der Frage kommt: Fluent Assertions: Compare two numeric collections approximately

+0

Wenn Sie in der Lage sind, zu verwenden 'float [] []', anstatt 'float [,]', dann können Sie auf der verlinkten Antwort erweitern so etwas wie haben: 'expectedArray.Should(). Gleich (calculatedArray, (rowLeft, rowRight) => {rowLeft.Should(). Equal (rowRight, (links, rechts) => Math.Abs ​​(links - rechts) <= 0.01); return true;}); ' – forsvarir

+0

@forsvarir Der float [,] wird verwendet, um die Elemente einer Matrix zu speichern. In diesem Fall bevorzuge ich das rechteckige Array, anstatt ein gezacktes Array zu verwenden. Obwohl es gut ist zu wissen, dass die Überlastung auf diese Weise verwendet werden kann. – Ayb4btu

Antwort

1

Es scheint nicht, etwas zu sein, derzeit in dem Rahmen, der dies unterstützt. Wenn Sie die Schleife nicht in Ihren Tests verwenden möchten, können Sie eigene Erweiterungen hinzufügen, um dieses Szenario abzudecken.

Es gibt zwei Elemente dazu. Die erste ist eine Erweiterungsmethode hinzuzufügen, die die Should Fähigkeit, 2D-Arrays ergänzt:

public static class FluentExtensionMethods 
{ 
    public static Generic2DArrayAssertions<T> Should<T>(this T[,] actualValue) 
    { 
     return new Generic2DArrayAssertions<T>(actualValue); 
    } 
} 

Sie dann die eigentliche Behauptung Klasse implementieren müssen, die die Vergleichs-Schleife enthält:

public class Generic2DArrayAssertions<T> 
{ 
    T[,] _actual; 

    public Generic2DArrayAssertions(T[,] actual) 
    { 
     _actual = actual; 
    } 

    public bool Equal(T[,] expected, Func<T,T, bool> func) 
    { 
     for (int i = 0; i < expected.Rank; i++) 
      _actual.GetUpperBound(i).Should().Be(expected.GetUpperBound(i), 
               "dimensions should match"); 

     for (int x = expected.GetLowerBound(0); x <= expected.GetUpperBound(0); x++) 
     { 
      for (int y = expected.GetLowerBound(1); y <= expected.GetUpperBound(1); y++) 
      { 
       func(_actual[x, y], expected[x, y]) 
        .Should() 
        .BeTrue("'{2}' should equal '{3}' at element [{0},{1}]", 
         x, y, _actual[x,y], expected[x,y]); 
      } 
     } 

     return true; 
    } 
} 

Sie können verwenden Sie es dann in Ihren Tests wie andere Behauptungen:

calculatedArray.Should().Equal(expectedArray, 
           (left,right)=> Math.Abs(left - right) <= 0.01); 

ich denke, Ihr Kommentar ist zu fragen, wie Sie testen Sie die Erweiterung gehen ich schlage. Die Antwort ist, genauso wie Sie etwas anderes testen, Werte übergeben und die Ausgabe validieren. Ich habe unten einige Tests hinzugefügt (mit Nunit), um einige Schlüsselszenarien abzudecken. Einige Dinge zu beachten, die Daten für die Szenarien sind unvollständig (es scheint außerhalb des Geltungsbereichs und ist nicht so schwer zu generieren). Die Tests verwenden eine Funktion von left == right, da der Punkt darin besteht, die Erweiterung zu testen, nicht die Auswertung der Approximation.

[TestCaseSource("differentSizedScenarios")] 
public void ShouldThrowIfDifferentSizes(float[,] actual, float[,] expected) 
{ 
    Assert.Throws<AssertionException>(()=>actual.Should().Equal(expected, (l, r) => l == r)).Message.Should().Be(string.Format("Expected value to be {0} because dimensions should match, but found {1}.", expected.GetUpperBound(0), actual.GetUpperBound(0))); 
} 

[TestCaseSource("missMatchedScenarios")] 
public void ShouldThrowIfMismatched(int[,] actual, int[,] expected, int actualVal, int expectedVal, string index) 
{ 
    Assert.Throws<AssertionException>(()=>actual.Should().Equal(expected, (l, r) => l.Equals(r))).Message.Should().Be(string.Format("Expected True because '{0}' should equal '{1}' at element [{2}], but found False.", actualVal, expectedVal, index)); 
} 

[Test] 
public void ShouldPassOnMatched() 
{ 
    var expected = new float[,] { { 3.1f, 4.5f }, { 2, 4 } }; 
    var actual = new float[,] { { 3.1f, 4.5f }, { 2, 4 } }; 
    actual.Should().Equal(expected, (l, r) => l.Equals(r)); 
} 

static object[] differentSizedScenarios = 
{ 
    new object[] { 
     new float[,] { { 3.1f, 4.5f }, { 2, 4 } }, 
     new float[,] { { 3.1f, 4.5f }, { 2, 4 }, {1,2} } 
    }, 
    new object[] { 
     new float[,] { { 3.1f, 4.5f }, { 2, 4 } }, 
     new float[,] { { 3.1f, 4.5f }} 
    } 
    // etc... 
}; 
static object[] missMatchedScenarios = 
{ 
    new object[] { 
     new int[,] { { 1, 2}, { 3, 4 } }, 
     new int[,] { { 11, 2}, { 3, 4 } } 
     ,1, 11, "0,0" 
    }, 
    new object[] { 
     new int[,] { { 1, 2}, { 3, 14 } }, 
     new int[,] { { 1, 2}, { 3, 4 } } 
     ,14, 4, "1,1" 
    }, 
    // etc... 
}; 
+0

Das ist eine schöne Lösung. Nun, um rekursiv zu sein, gibt es eine Möglichkeit, Komponententests zu schreiben, um sicherzustellen, dass dieser Code korrekt funktioniert? Oder ist dies ein Fall, in dem der Code manuell überprüft werden muss (ein fluentassertions Fail Case wird 'Generic2DArrayAssertions' beenden, bevor er die Assert in dem Komponententest, der ihn aufruft, trifft). – Ayb4btu

+0

@ Ayb4btu Ich denke, du fragst, wie man überprüft, dass die Erweiterung funktioniert, habe ich einige Tests hinzugefügt, um das Verhalten zu überprüfen, wenn dies nicht das ist, wonach Sie suchen, dann bin ich nicht sicher, ob ich Ihre Frage verstehe. – forsvarir

+0

Ja, das war es was ich gefragt habe. Ich wusste nicht, wie man die Fail-Case-Nachricht erfasst und verarbeitet, daher ist die .Message.Should().-Erweiterung ziemlich ordentlich. Danke für die tolle Antwort. – Ayb4btu