Чи є кращий спосіб написання одиничних тестів, ніж серія "AssertEquals"?


12

Ось основний приклад того, яким повинен бути мій тест з використанням квіт:

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Insert title here</title>

<link rel="stylesheet" href="qunit/qunit-1.13.0.css">
<script src = "qunit/qunit-1.13.0.js"></script>
<script src = "../js/fuzzQuery.js"></script>

<script>

test("Fuzz Query Basics", function()
        {
            equal(fuzzQuery("name:(John Smith)"), "name:(John~ Smith~)");
            equal(fuzzQuery("name:Jon~0.1"), "name:Jon~0.1");
            equal(fuzzQuery("Jon"), "Jon~");
            //etc

        }
    );

</script>
</head>
<body>
    <div id="qunit"></div>
</body>
</html>

Тепер я думав, що це трохи повторюється.

Міг би помістити всі входи / виходи в масив і пропустити через нього.

test("Fuzz Query Basics", function()
        {
            var equals = [
                           ["name:(John Smith)", "name:(John~ Smith~)"],
                           ["name:Jon~0.1", "name:Jon~0.1"],
                           ["Jon", "Jon~"]
                           ];

            for (var i = 0; i<equals.length; i++)
                {
                    equal(fuzzQuery(equals[i][0]), equals[i][1]);               
                }

        }
    );

І це чудово працює.

Єдиною перевагою, яку я можу придумати для цього другого методу, є те, що якщо виявиться, що ви насправді не хочете використовувати equal, простіше зробити цю зміну в одному місці.

Що стосується читабельності, я не думаю, що це не є переконливим, хоча я, мабуть, віддаю перевагу другому.

Абстрагуючи його далі, ви можете помістити випадки введення / виводу в окремий файл CSV, що може полегшити їх зміни.

Питання - що є загальними умовами щодо написання подібних одиничних тестів?

Чи є причина, що ви не повинні вводити їх у масиви?


Чи скаже вам будь-який із них, яке значення не вдалося?
JeffO

1
@JeffO - Так - З QUnit - Якщо тест не вдасться, результат показує очікуване значення та фактичне значення.
dwjohnston

Відповіді:


8

Ваші тести на відновлення мають запах: Умовна логіка випробувань .

Причини, з яких слід уникати запису умовної логіки у своїх тестах, є двократними. Перший полягає в тому, що це підриває вашу здатність бути впевненим, що ваш тестовий код правильний, як описано в пов'язаній статті xUnit Patterns.

Друга полягає в тому, що вона затьмарює значення тестів. Ми пишемо Методи тестування, оскільки вони ставлять логіку для тестування заданої поведінки в одному місці і дозволяють нам дати їй описову назву (див . Оригінальну статтю BDD Дена Норта для вивчення значення хороших імен для тестів). Коли ваші тести ховаються всередині однієї функції з forциклом, це затьмарює значення коду для читача. Читач не тільки повинен зрозуміти цикл, він також повинен подумки розгадати всі різні типи поведінки, що перевіряються в циклі.

Рішенням, як завжди, є підвищення рівня абстракції. Використовуйте основу тесту , який дає вам параметрірован тести , як xUnit.NET або контексти робити (відмова від відповідальності: я написав контексти). Це дозволяє групувати тести для тріангуляції однакової поведінки разом природним чином, зберігаючи окремі тести на окремі поведінки.


Добре запитання, до речі
Бенджамін Ходжсон

1
1) Якщо ви рухаєтесь на рівень абстракції, чи не ховаєте ви ті самі деталі, які, за вашими словами, затьмарені циклом for? 2) тут не застосовуються впевнені параметризовані тести. Здається, тут десь є паралелі, але у мене було багато ситуацій, схожих на ОП, де у мене був набір даних 10-20 значень і просто хотілося запустити їх через SUT. Так, кожне значення є різним і потенційно тестує різні майданчики, але, схоже, насправді "винайдіть" імена тестів для кожного окремого значення було б надмірним. Я знайшов оптимальне співвідношення значення / розмір коду, використовуючи подібні ...
DXM

... петлі. До тих пір, коли тест не вдасться, аргумент друкує саме те, що не вдалося, розробник має достатню кількість відгуків, щоб точно визначити проблему.
DXM

@DXM 1) тестова рамка забезпечує параметризовану функцію тесту. Ми довіряємо тестовій рамці неявно, тому не пишемо тестів для неї. 2) параметризовані тести саме для цієї мети: ви робите абсолютно однакові кроки кожного разу, але з різними значеннями введення / виводу. Тестова рамка економить вам необхідність писати імена для кожного з них, запускаючи різні входи через один і той же метод тестування.
Бенджамін Ходжсон

5

Схоже, ви дуже хочете перевірити одиницю даних, керовану даними. Оскільки ви згадали про використання QUnit, я знайшов плагін, який дозволяє параметризовані тести:

https://github.com/AStepaniuk/qunit-parameterize

З тестом, керованим даними, немає нічого ідеологічного, доки сам тестовий код не є умовним. З огляду на ваш тестовий код, здається, він є дуже хорошим кандидатом на тест, керований даними.

Приклад коду для GitHub README:

QUnit
    .cases([
        { a : 2, b : 2, expectedSum : 4 },
        { a : 5, b : 5, expectedSum : 10 },
        { a : 40, b : 2, expectedSum : 42 }
    ])
    .test("Sum test", function(params) {
        var actualSum = sum(params.a, params.b);
        equal(actualSum, params.expectedSum);
    });

1
Погоджено, це схоже на тест, керований даними. Але здається, що це вже є у своєму другому прикладі коду.
Роберт Харві

1
@RobertHarvey - правильно. Існує прийнятий термін для того, що він намагається досягти, і існує плагін для тестової системи, яка використовується для полегшення написання таких тестів. Я подумав, що варто зазначити у відповіді на майбутнє, ось і все.
Грег Бургхардт

1

Ви повторюєте себе менше, використовуючи масив, який є більш ретельним. Один із підходів, який я люблю використовувати, - це мати окремий метод, який упорядковує, діє та затверджує тести, але приймає вхідні параметри, з якими я тестую, тому у мене є 1 метод тестування на кожен вхідний набір.

Це дозволяє мені негайно сказати, які тести / входи не вдається.


0

Мені подобається твій другий підхід, але я додам 2 бали

  • не використовуйте масиви для зберігання перевірених даних, оскільки робота з індексами - це не зовсім чистий спосіб
  • не використовуйте forпетлі

`

[
    {
        process: "name:(John Smith)",
        result: "name:(John~ Smith~)"
    },
    {
        process: "name:Jon~0.1", 
        result: "name:Jon~0.1"
    },
    {
        process: "Jon", 
        result: "Jon~"
    }
]
.forEach(function(data){

    var result = fuzzQuery(data.process);
    equal(result, data.result);
});

Я не впевнений у квітні, але хороший тестовий бігун покаже, який вхідний рядок не вдався, і який очікуваний результат

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.