Почему этот модульный тест не проходит при сравнении двух двойных? - PullRequest
3 голосов
/ 30 марта 2011

У меня есть следующий код в vb.net, который вычисляет сумму до того, как был применен налог:

Public Shared Function CalculateRateBeforeTax(ByVal rate As Decimal, ByVal tax As Decimal) As Decimal
    Dim base As Decimal = rate / (1 + (tax / 100.0))
    Return Math.Round(base,2)
End Function

Некоторые сценарии, которые я настроил, были:

Ставка = 107, налог = 7%, база = 100

Ставка = 325, налог = 6,5%, база = 305,16

Ставка = 215, налог = 125%, база = 95,55

Я поместил вышеупомянутые сценарии в некоторые модульные тесты, используя c # и используя инфраструктуру тестирования nunit. Первый сценарий проходит, но другой терпит неудачу, и я не уверен, как я могу заставить его пройти. Вот мои тесты:

[TestFixture]
class TaxTests
{
    [Test]
    public void CalculateRateBeforeTax_ShouldReturn100_WhenRateIs107AndTaxIs7Percent()
    {
        decimal expected = 100.0m;
        decimal actual = TaxUtil.CalculateRateBeforeTax(107.0m, 7.0m);

        Assert.AreEqual(expected,actual);
    }

    [Test]
    public void CalculateRateBeforeTax_ShouldReturn305point16_WhenRateIs325AndTaxIs6point5Percent()
    {
        decimal expected = 305.16m;
        decimal actual = TaxUtil.CalculateRateBeforeTax(325.0m, 6.5m);

        Assert.AreEqual(expected, actual);
    }

    [Test]
    public void CalculateRateBeforeTax_ShouldReturn95point55_WhenRateIs215AndTaxIs125Percent()
    {
        decimal expected = 95.55m;
        decimal actual = TaxUtil.CalculateRateBeforeTax(215.0m, 125.0m);

        Assert.AreEqual(expected, actual);
    }

}

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

Второй тест ожидается 305.1600000000000003d Но было: 305.1643192488263d

Ожидается третий тест 95,54999999999997 Но было: 95,55555555555555557d

Ответы [ 4 ]

6 голосов
/ 30 марта 2011

Достаньте калькулятор и введите следующее: 325 / (1 + (6,5 / 100,0))

Результат 305.164319 ...

Тогда вы спрашиваете, равно ли 305.164319 ... 305.16. Тест явно не пройден, они не совпадают.

Теперь, если вам интересно, почему у вас немного другие цифры, чем 305.1600000000000003 вместо 305.16, это потому, что с типом Double есть некоторая потеря точности. Вы можете использовать десятичный тип для большей точности.

Но наиболее важной проблемой является то, что значение, возвращаемое CalculateRateBeforeTax, не усекается правильно, чтобы иметь точность до цента. Вам просто нужно усечь два знака после запятой вот так:

Dim rounded As Decimal = Math.Floor(base * 100) / 100

Теперь, изменив тип Double на тип Decimal, ваш Assert должен работать.

2 голосов
/ 30 марта 2011

Поздравления.Ваши юнит-тесты действительно сделали то, что должны, и обнаружили ошибку в коде, который вы тестируете.

У вас есть ошибки округления.К сожалению, это вызвано кодом VB.NET, который вы пытаетесь выполнить модульным тестом, а не кодом в реальных тестах.

Вам необходимо использовать тип данных с большей точностью.Я бы предложил заменить использование Double на Decimal.

0 голосов
/ 30 марта 2011

В то время как другие постеры правы, утверждая, что вы должны использовать Decimal вместо Double, что не является причиной вашей наблюдаемой проблемы.

Наблюдаемая проблема вызвана логическими ошибками при округлениикод.Вам нужно посмотреть, как правильно округлить такие значения.Это вопрос законов, а не математики.

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

0 голосов
/ 30 марта 2011

Вы не можете гарантировать, что числа с плавающей запятой совпадают друг с другом, даже если они выглядят так.

Это зависит от ряда факторов, таких как процессор, архитектура и т. Д. Как сказал Джастин, используйтедесятичная дробь, если требуется точность.

Посмотрите на отличное сообщение в блоге Джона Скитса: http://csharpindepth.com/Articles/General/FloatingPoint.aspx

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...