0

я могу приблизительно сравнить два 2D прямоугольных массивов в Fluent Assertions так:Свободно утверждения: Приблизительно сравнить два 2D прямоугольных массивов

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); 
    } 
} 

но есть уборщик способ достижения этой цели (без петель для)? Например, что-то в том же духе, как это (что для 1D массивов):

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); 

выше решение для 1D массив приходит от вопроса: Fluent Assertions: Compare two numeric collections approximately

+0

Если вы можете использовать 'плавать [] []', а не 'всплывают [,]', то вы можете расширить на связанный ответ на что-то вроде: 'expectedArray.Should(). Equal (calculateArray, (rowLeft, rowRight) => {rowLeft.Should(). Equal (rowRight, (влево, вправо) => Math.Abs ​​(слева направо) <= 0.01); return true;}); ' – forsvarir

+0

@forsvarir. Поля float [,] используются для хранения элементов матрицы, поэтому в этом случае я предпочитаю сохранить прямоугольный массив вместо использования неровного массива. Хотя хорошо знать, что перегрузка может быть использована именно так. – Ayb4btu

ответ

1

Там не кажется, ничего в настоящее время в рамках, которые поддерживают это. Если вы не хотите иметь цикл в своих тестах, тогда вам будет необходимо добавить собственные расширения для покрытия этого сценария.

Это два элемента. Во-первых, добавить метод расширения, который добавляет Should способность к 2D массивов:

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

Затем нужно осуществить фактический класс утверждение, которое будет содержать цикл сравнения:

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; 
    } 
} 

Вы можете затем использовать его в своих тестах, как и другие утверждения:

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

Я думаю, ваш комментарий спрашивает, как вы идете по поводу тестирования кода расширения я предлагаю. Ответ таков, как и вы тестируете что-либо еще, передавать значения и проверять результат. Я добавил несколько тестов ниже (используя Nunit), чтобы охватить некоторые ключевые сценарии. Некоторые вещи, которые следует учитывать, данные для сценариев являются неполными (они выглядят не по масштабам и не так сложно сгенерировать). В тестах используется функция left == right, так как точка заключается в проверке расширения, а не на оценке приближения.

[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

Это довольно приятное решение. Теперь, чтобы быть рекурсивным, существует ли способ написания модульных тестов для обеспечения правильного функционирования этого кода? Или это случай, когда нужно вручную проверять код (случай отказа от fluassassertions выйдет из 'Generic2DArrayAssertions', прежде чем нажать Assert в модульном тесте, который его вызывает). – Ayb4btu

+0

@ Ayb4btu Я думаю, вы спрашиваете, как проверить, работает ли расширение, я добавил некоторые тесты для проверки поведения, если это не то, что вам нужно, тогда я не уверен, что понимаю ваш вопрос. – forsvarir

+0

Да, это то, о чем я просил. Я не знал, как захватить и обработать сообщение об ошибке, так что расширение .Message.Should(). Быстрое расширение. Спасибо за отличный ответ. – Ayb4btu