Как я могу расширить класс NUnit Assert, добавив новую статическую перегрузку для AreEqual?
В настоящее время я разрабатываю оболочку С# P/invoke для DLL, которая является частью моего продукта. У меня нет опыта работы с С#, и это первый значительный код С#, который я сделал. Я остро осознаю, что мне не хватает знаний о тонкости и идиомах языка.
Мой вопрос касается модульных тестов, которые я пишу, для которых я использую NUnit. Мне нужно сравнить значения переменных double[]
. Если я использую Assert.AreEqual(...)
для этого, то значения сравниваются для точного равенства. Тем не менее, я бы хотел сравнить с допуском. Существуют AreEqual()
перегрузки для скалярных реальных значений, которые допускают параметр delta
. Однако я не смог найти эквивалент для массивов. Я пропустил что-то очевидное?
В настоящий момент я решил проблему со следующим кодом:
class Assert : NUnit.Framework.Assert
{
public static void AreEqual(double[] expected, double[] actual, double delta)
{
AreEqual(expected.Length, actual.Length);
for (int i = 0; i < expected.Length; i++)
{
AreEqual(expected[i], actual[i], delta);
}
}
}
Пока это работает, я задаюсь вопросом, есть ли более чистые решения. В частности, я обеспокоен тем, что использование одного и того же имени для моего производного класса - это не только плохой стиль, но и может привести к непредвиденным проблемам в будущем.
Мне бы хотелось использовать методы расширения, но я понимаю, что они жизнеспособны только тогда, когда есть экземпляр класса под расширением. Конечно, я только называю статические методы в классе Assert
.
Мне жаль, если это кажется немного туманным, но мои инстинкты говорят мне, что я не делаю этого наилучшим образом, и я хотел бы знать, как сделать это правильно.
Ответы
Ответ 1
С момента введения в NUnit плавного синтаксиса утверждения метод Within()
был доступен для этой цели:
double actualValue = 1.989;
double expectedValue = 1.9890;
Assert.That(actualValue, Is.EqualTo(expectedValue).Within(0.00001));
Assert.That(actualValue, Is.EqualTo(expectedValue).Within(1).Ulps);
Assert.That(actualValue, Is.EqualTo(expectedValue).Within(0.1).Percent);
Для коллекций поведение Is.EqualTo()
по умолчанию состоит в том, чтобы сравнивать элементы коллекций по отдельности, при этом эти отдельные сравнения изменяются на Within()
. Следовательно, вы можете сравнить два массива двойников так:
var actualDoubles = new double[] {1.0 / 3.0, 0.7, 9.981};
var expectedDoubles = new double[] { 1.1 / 3.3, 0.7, 9.9810};
Assert.That(actualDoubles, Is.EqualTo(expectedDoubles).Within(0.00001));
Assert.That(actualDoubles, Is.EqualTo(expectedDoubles).Within(1).Ulps);
Assert.That(actualDoubles, Is.EqualTo(expectedDoubles).Within(0.1).Percent);
Это будет сравнивать каждый элемент actualDoubles
с соответствующим элементом в expectedDoubles
с использованием указанного допуска и не удастся, если какой-либо из них недостаточно закрыт.
Ответ 2
Я думаю, что то, что я сделал бы, просто определит функцию где-нибудь в тестовой проводке
public static bool AreEqual( double[], double[], double delta )
который выполняет сравнение и возвращает true или false соответствующим образом. В своем тесте вы просто пишете:
Assert.IsTrue( AreEqual( expected, result, delta ) ) ;
Ответ 3
"Лучше" всегда дело вкуса. В этом случае я бы сказал "да". Вы должны сделать свое утверждение, не подклассифицируя nunit assert. Nunit уже имеет несколько классов Assert с различными конкретными утверждениями. Как CollectionAssert. В противном случае ваш метод в порядке.
Ответ 4
Мне нужно было создать пользовательское утверждение, в вашем случае была альтернатива, предоставляемая инфраструктурой. Однако это не сработало, когда я хотел иметь полностью настраиваемый аргумент. Я решил это, добавив новый вызов статического класса в nunit.
public static class FooAssert
{
public static void CountEquals(int expected, FooConsumer consumer)
{
int actualCount = 0;
while (consumer.ConsumeItem() != null)
actualCount++;
NUnit.Framework.Assert.AreEqual(expected, actualCount);
}
}
Тогда в тесте
[Test]
public void BarTest()
{
// Arrange
...
// Act
...
// Assert
FooAssert.CountEquals(1, fooConsumer);
}
Я знаю, что я немного опаздываю на вечеринку, это может быть полезно для кого-то