There doesn't appear to be anything currently in the framework that supports this. If you don't want to have the loop in your tests, then one option would be for you to add your own extensions to cover this scenario.
There are two elements to this. The first is to add an extension method that adds the Should
ability to 2D arrays:
public static class FluentExtensionMethods
{
public static Generic2DArrayAssertions<T> Should<T>(this T[,] actualValue)
{
return new Generic2DArrayAssertions<T>(actualValue);
}
}
You then need to implement the actual assertion class, which will contain the comparison loop:
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;
}
}
You can then use it in your tests like other assertions:
calculatedArray.Should().Equal(expectedArray,
(left,right)=> Math.Abs(left - right) <= 0.01);
I think your comment is asking how you go about testing the extension code I'm suggesting. The answer is, the same way you go about testing anything else, pass in values and validate the output. I've added some tests below (using Nunit) to cover some of the key scenarios. Some things to note, the data for the scenarios is incomplete (it seems out of scope and isn't that hard to generate). The tests are using a func of left == right
, since the point is to test the extension, not the evaluation of the 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...
};