Автотесты на double не проходят

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

TestDeleniye(145,87,1.66666666666667d) Источник: UnitTest1.cs строка 147 Длительность: 8,6 с Сообщение: Expected string length 16 but was 18. Strings differ at index 1. Expected: "1,66666666666667" But was: "1.6666666666666667"

        [TestCase(25, 5, 5)]
        [TestCase(50, 0, "Infinity")]
        [TestCase(854, 96, 8.89583333333333)]
        [TestCase(50, 15, 3.3333333333333335)]
        [TestCase(95, 25, 3.8)]
        [TestCase(25, 2, 12.5)]
        [TestCase(145, 87, 1.66666666666667)]
        [TestCase(524, 325, 1.612307692307692)]
        [TestCase(320, 120, 2.666666666666666)]
        [TestCase(1254, 9652, 0.1299212598425196)]
        [TestCase(15487, 9654, 1.604205510669152)]


        public void TestDeleniye(double a, double b, object c)
        {
            IWebDriver driver;
            driver = new ChromeDriver();
            driver.Manage().Window.Maximize();
            driver.Navigate().GoToUrl("file:///C:/Users/HP/Desktop/Calcul2/index.html");

            foreach (char ch in a.ToString())
            {
                driver.FindElement(By.XPath($"//button[@value='{ch}']")).Click();
            }
            driver.FindElement(By.XPath("//button[@value='/']")).Click();

            foreach (char ch in b.ToString())
            {
                driver.FindElement(By.XPath($"//button[@value='{ch}']")).Click();
            }
            driver.FindElement(By.XPath("//button[@value='=']")).Click();

            IWebElement flashMessage = driver.FindElement(By.XPath("//Input[@type='text']"));
            string actual = flashMessage.GetAttribute("value");
            NUnit.Framework.Assert.AreEqual(c.ToString(), actual);

            driver.Quit();
        }

Ответы (1 шт):

Автор решения: A K

То, о чём говорил вам @Bulson.

Нельзя писать тесты на числа с плавающей запятой сравнивая непосредственно значения:

    [TestCase(145, 87, 1.66666666666667)]
    public void DivisionTest1(double a, double b, double expected)
    {
        double actual = a / b;
        Assert.AreEqual(expected, actual);
    }

Необходимо задать некоторую констранту, точность вычисления (Precision), искать разницу (delta, difference) между ожидаемым (expected) и реальным (actual) значением и проверять что разность не больше чем заданная точность.

Вот такой тест проходит нормально:

public class CalculatorTest
{
    private const double Precision = 0.000001;

    [TestCase(145, 87, 1.66666666666667)]
    public void DivisionTest2(double a, double b, double expected)
    {
        double actual     = a / b;
        double difference = Math.Abs(actual - expected);
        Assert.LessOrEqual(difference, Precision);
    }
}

или:

public class CalculatorTest
{
    private const double Precision = 0.000001;

    [TestCase(145, 87, 1.66666666666667)]
    public void DivisionTest2(double a, double b, double expected)
    {
        double actual = a / b;
        Assert.AreEqual(expected, actual, Precision);
    }
}

Ну и обратите внимание, что если у вас точность до шестого знака, то в TestCase нет смысла указывать точность на 10 знаков. Это анекдотично "15,04 грамма с точностью плюс-минус килограмм".

Выбор количества нулей когда вы задаёте точность диктуется техническим заданием на проверяемую операцию. Например, при написании тестов на финансовые операции можно выбрать погрешность "меньше чем копейка" (0,1 копейки), в банках берут погрешность ещё меньше и т.п.


Могу ещё дать пару советов по поводу чистоты кода. Код слишком длинный и содержит очень много подробностей как конкретно вы получаете свой реальный результат.

Рекомендую вам вынести всю эту логику хромдрайвера в отдельный метод в базовом классе и в тестах вам достаточно будет только дать одну строку

double actual = Divide(a, b);

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

Вот скажите, вам же проще будет читать такой тест:

    public void DivisionTest(double a, double b, double expected)
    {
        ClickNumber(a);
        ClickDivideOperation();
        ClickNumber(b);
        ClickEnter();

        var actual = GetResults();
        Assert.AreEqual(expected, actual, Precision);
    }

где в базовый класс вы поместите определения методов:

    protected void ClickNumber(double number)
    {
        foreach (char ch in number.ToString())
        {
            Driver.FindElement(By.XPath($"//button[@value='{ch}']")).Click();
        }
    }

    protected void ClickDivideOperation()
    {
        Driver.FindElement(By.XPath("//button[@value='/']")).Click();
    }

    protected void ClickEnter()
    {
        Driver.FindElement(By.XPath("//button[@value='=']")).Click();
    }

чем постоянно переписывать однотипное driver.FindElement(By. Программист должен стараться писать переиспользуемый код, а не копипасту.

И код методов можно ещё больше упростить, если выделить метод кликающий конкретную кнопку:

    protected void ClickNumber(double number)
    {
        foreach (char ch in number.ToString())
        {
            ClickButton(ch);
        }
    }

    protected void ClickDivideOperation()
    {
        ClickButton('/');
    }

    protected void ClickEnter()
    {
        ClickButton('=');
    }

    private void ClickButton(char ch)
    {
        Driver.FindElement(By.XPath($"//button[@value='{ch}']")).Click();
    }

Так потихоньку небольшим рефакторингом (используя паттерн "строитель") можно придти к классическим AAA-тестам:

    public void DivisionTest(double a, double b, double expected)
    {
        // Arrange
        var calculator = new Calculator();

        // Act
        var actual = calculator.ClickNumber(a)
                               .ClickDivideOperation()
                               .ClickNumber(b)
                               .ClickEnter()
                               .GetResults();

        // Assert
        Assert.AreEqual(expected, actual, Precision);
    }

Второй момент: не сравнивайте строки, здесь много тонких моментов, связанных с отличиями в культуре (точка/запятая в качестве разделителя целой и дробной части, форматирование тысяч и т.п.), а также разницей в регистре букв (если бы вы писали калькулятор 16ричной системы - было бы ещё больнее).

Язык вам предоставляет средства по строгой типизации, поэтому не используйте строки, используйте double. Если где-то из недр хром драйвера приходит строка -- вовремя распарсите её и дальше оперируйте уже только double.


О тестах на деление на ноль. Во-первых, у вас написаны интеграционные тесты, которые тестируют полностью приложение, а не юнит-тесты на бизнес-логику. Обычно в приложении пишутся сначала тесты на бизнес-логику (логику домена приложения), а потом постепенно дополняют интеграционными тестами.

Поэтому вам рекомендуется разделить тесты на два слоя и начать с тестирования бизнес-логики. Деление на ноль выбрасывает исключение, поэтому не стоит писать TestCase - этот тест нужно написать отдельно:

    [Test]
    public void DivisionByZero_ThrowsExection()
    {
        Assert.Throws<DivideByZeroException>(() => Division(42, 0));
    }

(И вообще, это деление int на 0 даёт такое исключение, если у вас double то просто будет бесконечно большое число, используйте константу double.MaxValue)

И уже во вторую очередь стоит думать как отлавливать такие исключения из кода сайта в интеграционных тестах.

→ Ссылка