Чи добре мати кілька тверджень в одному тесті?


397

У коментарі до цього чудового допису Рой Ошерово згадав проект OAPT, який призначений для запуску кожного твердження в одному тесті.

На домашній сторінці проекту написано:

Правильні одиничні тести повинні бути невдалими саме з однієї причини, тому вам слід використовувати одне твердження за одиницю тесту.

А також Рой написав у коментарях:

Моє правило, як правило, ви перевіряєте один логічний ПОНЯТТЯ за тест. ви можете мати кілька тверджень на одному об’єкті . вони зазвичай будуть тією ж концепцією, яку перевіряють.

Я думаю, що є деякі випадки, коли потрібні кілька тверджень (наприклад, твердження про охорону ), але в цілому я намагаюся цього уникати. Яка ваша думка? Наведіть приклад реального світу, коли дійсно потрібно кілька тверджень .


2
Як ви знущаєтесь, не маючи декількох тверджень? Кожне очікування макету - це твердження саме по собі, включаючи будь-який порядок дзвінків, які ви нав'язуєте.
Крістофер Крейціг

15
Я бачив, як філософія «за твердженням за методом» зловживала в минулому. Старий колега працював із прикольним механізмом успадкування, щоб зробити це можливим. Це призвело до безлічі підкласів (один на галузь) та безлічі тестів, які проводили той самий процес налаштування / руйнування лише для перевірки різних результатів. Це було повільно, важко для читання і серйозна проблема у обслуговуванні. Я ніколи не переконував його перейти до більш класичного підходу. Книга Джерарда Мецароса докладно розповідає про цю тему.
Travis Parks

3
Я думаю, як загальне правило, ви повинні намагатися мінімізувати кількість тверджень за тест. Однак, поки тест достатньо звужує проблему до певного місця в коді, тоді це корисний тест.
ConditionRacer

2
Я бачив випадки, коли замість RowTest(MbUnit) / TestCase(NUnit) було використано кілька тверджень для тестування різних типів поведінки. Використовуйте належні інструменти для роботи! (На жаль, MSTest, здається, ще не має можливості тестування рядків.)
GalacticCowboy

@GalacticCowboy Ви можете отримати подібну функціональність RowTestта TestCaseвикористовувати тестові джерела даних . Я використовую простий файл CSV з великим успіхом.
julealgon

Відповіді:


236

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

Сказавши це, я б сказав, що, можливо, половина моїх тестів має лише одне твердження. Я думаю, що він стає кодовим (тестовим?) Запахом лише тоді, коли у вашому тесті є близько п'яти і більше тверджень.

Як ви вирішуєте кілька тверджень?


9
Як і ця відповідь - тобто її добре, але загалом це не добре (-:
Мерф

5
Так? Навіщо ти це робив? виконання методу точно таке ж?
jgauffin

197
Одне твердження на одиницю тесту - чудовий спосіб перевірити здатність читача прокручуватися вгору та вниз.
Том

3
Якісь міркування за цим? У цій нинішній відповіді просто зазначено, що це має бути, але не чому.
Стівен Євріс

37
Категорично не згоден. У відповіді не вказано жодної переваги наявності єдиного твердження, і очевидним недоліком є ​​те, що вам потрібно скопіювати і вставити тести лише тому, що відповідь в Інтернеті так говорить. Якщо вам потрібно протестувати декілька полів результату або декілька результатів однієї операції, вам абсолютно слід стверджувати всі їх у незалежних твердженнях, оскільки це дає набагато більше корисної інформації, ніж тестування їх у великому блоці-ствердженні. У хорошому тесті ви перевіряєте одну операцію, а не один результат операції.
Пітер

296

Тести повинні провалюватися лише з однієї причини, але це не завжди означає, що має бути лише одне Assertтвердження. ІМХО важливіше дотримуватися схеми " Упорядкувати, діяти, підтверджувати ".

Ключовим є те, що у вас є лише одна дія, а потім ви перевіряєте результати цієї дії, використовуючи твердження. Але це "Упорядкувати, діяти, підтвердити, закінчити тест ". Якщо ви спокушаєтесь продовжувати тестування, виконуючи іншу дію і після цього більше заявляйте, зробіть замість цього окремий тест.

Я радий бачити кілька тверджень про твердження, які є частинами тестування однієї дії. напр

[Test]
public void ValueIsInRange()
{
  int value = GetValueToTest();

  Assert.That(value, Is.GreaterThan(10), "value is too small");
  Assert.That(value, Is.LessThan(100), "value is too large");
} 

або

[Test]
public void ListContainsOneValue()
{
  var list = GetListOf(1);

  Assert.That(list, Is.Not.Null, "List is null");
  Assert.That(list.Count, Is.EqualTo(1), "Should have one item in list");
  Assert.That(list[0], Is.Not.Null, "Item is null");
} 

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

наприклад, перший може бути

Assert.IsTrue((10 < value) && (value < 100), "Value out of range"); 

Але це не краще - повідомлення про помилку з нього менш конкретне, і воно не має інших переваг. Я впевнений, що ви можете придумати інші приклади, коли поєднання двох-трьох (або більше) тверджень в одне велике булеве умова ускладнює читання, складніше переробляє і важче роз'яснює, чому це не вдалося. Чому це робиться тільки заради правила?

NB : Код, про який я тут пишу, - це C # з NUnit, але принципи будуть дотримуватися інших мов та рамок. Синтаксис теж може бути дуже схожим.


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

1
Я думаю, що також цікаво мати більше одного сертифіката, якщо Arrange - це дорого.
Рекшино

1
@Rekshino, якщо аранжування коштує дорого, ми можемо поділитися кодом аранжування, наприклад, помістивши код аранжування в процедуру ініціалізації тесту.
Shaun Luttin

2
Отже, якщо я порівнюю з відповіддю Яко, "лише один твердження" стає "лише однією групою тверджень", які мають для мене більше сенсу.
Вальфрат

2
Це хороша відповідь, але я не погоджуюся з тим, що окремі твердження не є кращими. Приклад поганого твердження не є кращим, але це не означає, що одне твердження не було б краще, якщо зробити все правильно. Багато бібліотек дозволяють користувальницьким затвердженням / відповідникам, щоб щось можна було створити, якщо його ще немає. Наприклад , Assert.IsBetween(10, 100, value)що друкує Expected 8 to be between 10 and 100 це краще , ніж дві окремі стверджує , на мій погляд. Можна, звичайно, стверджувати, що це не потрібно, але зазвичай варто подумати, чи легко його звести до одного твердження, перш ніж скласти цілий набір з них.
Thor84no

85

Я ніколи не думав, що більше одного твердження було поганою справою.

Я роблю це постійно:

public void ToPredicateTest()
{
    ResultField rf = new ResultField(ResultFieldType.Measurement, "name", 100);
    Predicate<ResultField> p = (new ConditionBuilder()).LessThanConst(400)
                                                       .Or()
                                                       .OpenParenthesis()
                                                       .GreaterThanConst(500)
                                                       .And()
                                                       .LessThanConst(1000)
                                                       .And().Not()
                                                       .EqualsConst(666)
                                                       .CloseParenthesis()
                                                       .ToPredicate();
    Assert.IsTrue(p(ResultField.FillResult(rf, 399)));
    Assert.IsTrue(p(ResultField.FillResult(rf, 567)));
    Assert.IsFalse(p(ResultField.FillResult(rf, 400)));
    Assert.IsFalse(p(ResultField.FillResult(rf, 666)));
    Assert.IsFalse(p(ResultField.FillResult(rf, 1001)));

    Predicate<ResultField> p2 = (new ConditionBuilder()).EqualsConst(true).ToPredicate();

    Assert.IsTrue(p2(new ResultField(ResultFieldType.Confirmation, "Is True", true)));
    Assert.IsFalse(p2(new ResultField(ResultFieldType.Confirmation, "Is False", false)));
}

Тут я використовую кілька тверджень, щоб переконатися, що складні умови можуть бути перетворені на очікуваний предикат.

Я тестую лише одну одиницю ( ToPredicateметод), але я висвітлюю все те, що я можу придумати в тесті.


46
Кілька тверджень погані через виявлення помилок. Якщо ви не вдалися до свого першого Assert.IsTrue, інші твердження не виконуватимуться, і ви не отримаєте від них жодної інформації. З іншого боку, якщо у вас було 5 тестів замість 1 з 5 твердженнями, ви можете отримати щось корисне
Sly

6
Чи вважаєте ви все ще поганим, якщо всі твердження перевіряють однаковий функціонал? Як і вище, приклад тестує умовні умови, і якщо щось із цього не вдається, вам слід виправити це. Чи важливо для вас, що ви можете пропустити останні 2 твердження, якщо попереднє не вдалося?
притулок

106
Я виправляю свої проблеми по черзі. Тож факт, що тест міг провалитись не один раз, мене не турбує. Якщо я розділю їх, у мене з’являться однакові помилки, але все відразу. Мені легше виправити речі за кроком. Я визнаю, що в цьому випадку останні два твердження, можливо, можуть бути перероблені на власний тест.
Метт Еллен

19
Ваш випадок дуже представницький, тому NUnit має додатковий атрибут TestCase - nunit.org/?p=testCase&r=2.5
Restuta

10
google C ++ для тестування має ASSERT () та EXPECT (). ASSERT () зупиняється на збої, а EXPECT () продовжується. Це дуже зручно, коли ви хочете перевірити кілька тестів у тесті.
ratkok

21

Коли я використовую тестування одиниць для перевірки поведінки на високому рівні, я абсолютно кладу кілька тверджень в один тест. Ось тест, який я фактично використовую для деякого коду повідомлення про надзвичайні ситуації. Код, який працює перед тестом, приводить систему в стан, коли, якщо запускається основний процесор, надсилається тривога.

@Test
public void testAlarmSent() {
    assertAllUnitsAvailable();
    assertNewAlarmMessages(0);

    pulseMainProcessor();

    assertAllUnitsAlerting();
    assertAllNotificationsSent();
    assertAllNotificationsUnclosed();
    assertNewAlarmMessages(1);
}

Він представляє умови, які необхідно існувати на кожному кроці процесу, щоб я був впевнений, що код поводиться так, як я очікую. Якщо одне твердження не вдасться, мені байдуже, що решта навіть не запустяться; оскільки стан системи вже не дійсний, ці наступні твердження не дадуть мені нічого цінного. * Якщо assertAllUnitsAlerting()не вдалося, я б не знав, що робити з assertAllNotificationSent()успіхом АБО невдачею, поки я не визначив, що спричиняло попередню помилку і виправили це.

(* - Гаразд, вони, можливо, можуть бути корисними для налагодження проблеми. Але найголовніша інформація про тест не вдалася, вже отримана.)


Коли ви зробите це, вам слід краще використовувати тестування фреймворків із залежними тестами, то краще (наприклад, testng підтримує цю функцію)
Kemoda

8
Я теж пишу такі тести, щоб ви могли бути впевнені в тому, що робить код, і зміни стану, я не думаю, що це тест на одиницю, а на інтеграційний тест.
mdma

Які ваші думки щодо рефакторингу його на assertAlarmStatus (int numberOfAlarmMessages) ;?
Борджаб

1
Ваші твердження склали б дуже хороші імена для тесту.
Бьорн Тіплінг

Краще відпустити тест навіть на недійсному введенні. Це дає вам більше інформації таким чином (особливо, якщо вона все ще проходить, коли ви цього не очікували).
CurtainDog

8

Ще одна причина, чому я думаю, що кілька тверджень в одному методі - це не погано, описано в наступному коді:

class Service {
    Result process();
}

class Result {
    Inner inner;
}

class Inner {
    int number;
}

У своєму тесті я просто хочу перевірити, чи service.process()повертається правильне число в Innerекземплярах класу.

Замість тестування ...

@Test
public void test() {
    Result res = service.process();
    if ( res != null && res.getInner() != null ) Assert.assertEquals( ..., res.getInner() );
}

Я роблю

@Test
public void test() {
    Result res = service.process();
    Assert.notNull(res);
    Assert.notNull(res.getInner());
    Assert.assertEquals( ..., res.getInner() );
}

2
І це добре, у вас не повинно бути жодної умовної логіки у ваших тестах. Це робить тест складнішим і менш читабельним. І я думаю, що Рой окреслював це прямо у своєму дописі в блозі, що більшість випадків із заявою про один об'єкт є нормальним. Тож ті, що у вас є, - це лише твердження про охорону, і нормально їх мати.
Рестута

2
Ваші Assert.notNulls зайві, ваш тест закінчиться програмою NPE, якщо вони недійсні.
сара

1
Крім того, ваш перший приклад (з if) буде передано, якщо resєnull
Сара

1
@kai notNull є зайвими, я погоджуюся, але я вважаю, що чистіше мати затвердження (і якщо я не лінуюся також з належним повідомленням) замість винятку ...
Betlista

1
Ну невдале твердження також викидає виняток, і в обох випадках ви отримуєте пряме посилання на точний рядок, який кинув його із супроводжуючим слідом стека, так що особисто я не хотів би захаращувати тест із передумовами, які все одно перевіряться. Я вважаю за краще oneliner à la Assert.assertEquals(..., service.process().getInner());, можливий із вилученими змінними, якщо рядок стає "занадто довгим"
Сара

6

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

Наприклад, уявіть собі функцію, яка аналізує рядок дати:

function testParseValidDateYMD() {
    var date = Date.parse("2016-01-02");

    Assert.That(date.Year).Equals(2016);
    Assert.That(date.Month).Equals(1);
    Assert.That(date.Day).Equals(0);
}

Якщо тест не вдається, це з однієї причини, синтаксичний аналіз є неправильним. Якщо ви стверджуєте, що цей тест може бути невдалим з трьох різних причин, ви б IMHO був надто тонкий у своєму визначенні "однієї причини".


3

Я не знаю жодної ситуації, де було б гарною ідеєю мати кілька тверджень всередині самого методу [Тест]. Основна причина, чому люди люблять мати декілька тверджень - це те, що вони намагаються мати один клас [TestFixture] для кожного класу, який тестується. Натомість ви можете розбити свої тести на більше [TestFixture] класів. Це дозволяє вам бачити кілька способів, в яких код може не реагував так, як ви очікували, а не лише той, де перше твердження не вдалося. Як ви цього досягаєте, у вас є принаймні один каталог у класі, який тестується з великою кількістю класів [TestFixture] всередині. Кожен клас [TestFixture] буде названий відповідно до конкретного стану об'єкта, який ви будете тестувати. Метод [SetUp] переведе об'єкт у стан, описаний іменем класу. Тоді у вас є кілька методів [Тест], кожен із яких стверджує різні речі, які, як ви очікували б, були правдивими, враховуючи поточний стан об'єкта. Кожен метод [Тест] має назву за те, що він стверджує, за винятком того, що він може бути названий за концепцією, а не лише англійською мовою зчитування коду. Тоді для реалізації кожного методу [Test] потрібен лише один рядок коду, де він щось стверджує. Ще одна перевага цього підходу полягає в тому, що він робить тести дуже читабельними, оскільки стає цілком зрозумілим, що ви тестуєте, і що ви очікуєте, просто переглянувши назви класів та методів. Це також покращиться, коли ви почнете усвідомлювати всі маленькі кейси, які ви хочете перевірити, і коли ви знайдете помилок. за винятком, можливо, він може бути названий на честь концепції, а не лише англійською мовою зчитування коду. Тоді для реалізації кожного методу [Test] потрібен лише один рядок коду, де він щось стверджує. Ще одна перевага цього підходу полягає в тому, що він робить тести дуже читабельними, оскільки стає цілком зрозумілим, що ви тестуєте, і що ви очікуєте, просто переглянувши назви класів та методів. Це також покращиться, коли ви почнете усвідомлювати всі маленькі кейси, які ви хочете перевірити, і коли ви знайдете помилок. за винятком, можливо, він може бути названий на честь концепції, а не лише англійською мовою зчитування коду. Тоді для реалізації кожного методу [Test] потрібен лише один рядок коду, де він щось стверджує. Ще одна перевага цього підходу полягає в тому, що він робить тести дуже читабельними, оскільки стає цілком зрозумілим, що ви тестуєте, і що ви очікуєте, просто переглянувши назви класів та методів. Це також покращиться, коли ви почнете усвідомлювати всі маленькі кейси, які ви хочете перевірити, і коли ви знайдете помилок. і що ви очікуєте, просто переглянувши назви класів та методів. Це також покращиться, коли ви почнете усвідомлювати всі маленькі кейси, які ви хочете перевірити, і коли ви знайдете помилок. і що ви очікуєте, просто переглянувши назви класів та методів. Це також покращиться, коли ви почнете усвідомлювати всі маленькі кейси, які ви хочете перевірити, і коли ви знайдете помилок.

Зазвичай це означає, що підсумковий рядок коду всередині методу [SetUp] повинен зберігати значення властивості або повертати значення у змінній приватного екземпляра [TestFixture]. Тоді ви можете стверджувати кілька різних речей про цю змінну екземпляра від різних методів [Test]. Ви також можете зробити твердження про те, які різні властивості випробуваного об'єкта встановлені тепер, коли він знаходиться в бажаному стані.

Іноді вам потрібно робити твердження по ходу, коли ви отримуєте тестуваний об’єкт у потрібний стан, щоб переконатися, що ви не зіпсувались, перш ніж повернути об’єкт у потрібний стан. У цьому випадку ці додаткові твердження повинні з’являтися всередині методу [SetUp]. Якщо всередині методу [SetUp] щось піде не так, буде зрозуміло, що з тестом щось було не так, перш ніж об’єкт колись перейшов у потрібний стан, який ви мали намір перевірити.

Ще одна проблема, з якою ви можете зіткнутися, - це те, що ви протестуєте виняток, який ви очікували, що вас кинуть. Це може спокусити вас не дотримуватися вищевказаної моделі. Однак цього все-таки можна досягти, зафіксувавши виняток всередині методу [SetUp] та зберігаючи його в змінній примірника. Це дозволить вам стверджувати різні речі про виняток, кожен із яких має свій власний метод [Test]. Потім ви також можете стверджувати інші речі про тестований об’єкт, щоб переконатися, що не було випадкових побічних ефектів від викинутого винятку.

Приклад (це буде розбито на кілька файлів):

namespace Tests.AcctTests
{
    [TestFixture]
    public class no_events
    {
        private Acct _acct;

        [SetUp]
        public void SetUp() {
            _acct = new Acct();
        }

        [Test]
        public void balance_0() {
            Assert.That(_acct.Balance, Is.EqualTo(0m));
        }
    }

    [TestFixture]
    public class try_withdraw_0
    {
        private Acct _acct;
        private List<string> _problems;

        [SetUp]
        public void SetUp() {
            _acct = new Acct();
            Assert.That(_acct.Balance, Is.EqualTo(0));
            _problems = _acct.Withdraw(0m);
        }

        [Test]
        public void has_problem() {
            Assert.That(_problems, Is.EquivalentTo(new string[] { "Withdraw amount must be greater than zero." }));
        }

        [Test]
        public void balance_not_changed() {
            Assert.That(_acct.Balance, Is.EqualTo(0m));
        }
    }

    [TestFixture]
    public class try_withdraw_negative
    {
        private Acct _acct;
        private List<string> _problems;

        [SetUp]
        public void SetUp() {
            _acct = new Acct();
            Assert.That(_acct.Balance, Is.EqualTo(0));
            _problems = _acct.Withdraw(-0.01m);
        }

        [Test]
        public void has_problem() {
            Assert.That(_problems, Is.EquivalentTo(new string[] { "Withdraw amount must be greater than zero." }));
        }

        [Test]
        public void balance_not_changed() {
            Assert.That(_acct.Balance, Is.EqualTo(0m));
        }
    }
}

Як ви обробляєте вхід TestCase в цьому випадку?
Саймон Гіллбі

Таким чином, у моїй теперішній організації у нас є 20 000+ одиничних тестів, дуже схожих на те, що ви показуєте. Це кошмар. Значна частина коду настройки тесту скопійована / вставлена, що призводить до неправильної установки тесту та недійсних тестів, які проходять. Для кожного [Test]методу цей клас повторно інстанціюється і [SetUp]метод виконується заново. Це вбиває .NET Garbage Collector і змушує тести працювати дуже повільно: 5+ хвилин локально, 20+ хвилин на сервері збірки. Тести на 20К повинні працювати приблизно за 2 - 3 хвилини. Я б зовсім не рекомендував цей стиль тестування, особливо для тестового набору з великим результатом.
чотирипаночі

@fourpastmidnight більшість того, що ви сказали, здається вагомою критикою, але суть про копіювання та вставлення коду настройки та, отже, помиляється, це не проблема структури, а безвідповідальних програмістів (що може бути наслідком безвідповідальних менеджерів чи негативного середовища більше, ніж погані програмісти). Якщо люди просто копіюють і вставляють код і сподіваються, що він правильний і не намагаються зрозуміти код, в будь-якому контексті з будь-якої причини, їм потрібно або навчитись цього не робити, або відпустити, якщо вони не можуть бути навчений. Це суперечить кожному доброму принципу програмування.
still_dreaming_1

Але загалом, я погоджуюся, це божевільний надмір, який спричинить за собою велику кількість здуття / багажу / дублювання, що призведе до всіляких проблем. Я звик бути божевільним і рекомендував подібні речі. Це те, що я сподіваюся, що можу щодня говорити про себе напередодні, бо це означає, що я ніколи не перестаю знаходити кращих способів робити справи.
still_dreaming_1

@ still_dreaming_1 wrt "безвідповідальні програмісти": Я згоден, що така поведінка є головною проблемою. Однак така структура тесту дійсно запрошує такого поведінки на краще чи гірше. Погана практика розробки в бік мого основного заперечення проти цієї форми полягає в тому, що вона дійсно вбиває тестові показники. Немає нічого гіршого, ніж тестовий набір для повільної роботи. Набір тестів для повільного запуску означає, що люди не будуть запускати тести локально і навіть спокушаються пропустити їх на проміжних складах - знову ж таки, проблема з людьми, але це трапляється - чого все можна уникнути, забезпечивши швидкі запущені тести для запуску з.
чотирипасти середньоночі

2

Маючи кілька тверджень в одному тесті - це лише проблема, коли тест не вдається. Тоді вам, можливо, доведеться налагодити тест або проаналізувати виняток, щоб з’ясувати, яке твердження не відповідає. З одним твердженням у кожному тесті, як правило, простіше визначити, що не так.

Я не можу придумати сценарій, коли дійсно потрібно кілька тверджень , оскільки ви завжди можете переписати їх як кілька умов в одному твердженні. Однак, можливо, буде кращим, якщо у вас, наприклад, є кілька кроків, щоб перевірити проміжні дані між кроками, а не ризикувати, що наступні кроки вийдуть із-за поганого введення.


1
Якщо ви поєднуєте декілька умов в одне твердження, то при відмові все, що ви знаєте, - це те, що не вдалося. З декількома твердженнями ви конкретно знаєте про деякі з них (від тих, що доходили і включали помилку). Розглянемо, перевірка повернутого масиву містить одне значення: перевірте, чи він не є нульовим, тоді він має саме один елемент, а потім значення цього елемента. (Залежно від платформи) просто перевірка значення негайно може дати нульову відхилення (менш корисна, ніж помилка твердження про нуль) і не перевіряє довжину масиву.
Річард

@Richard: Отримання результату, а потім вилучення чогось із цього результату було б процесом у кілька етапів, тому я висвітлював це у другому абзаці у відповіді.
Guffa

2
Правило: якщо у вас є кілька тверджень у тесті, кожне з них повинно мати інше повідомлення. Тоді у вас немає цієї проблеми.
Ентоні

А використовуючи тест якості тесту, такий як NCrunch, вам точно покаже, на якому рядку тест не вдався, як у тестовому коді, так і в тестовому коді.
чотирипаночі

2

Якщо ваш тест не вдасться, ви також не знатимете, чи не порушуватимуться наступні твердження. Часто це означає, що вам не вистачає цінної інформації, щоб з’ясувати джерело проблеми. Моє рішення - використовувати одне твердження, але з кількома значеннями:

String actual = "val1="+val1+"\nval2="+val2;
assertEquals(
    "val1=5\n" +
    "val2=hello"
    , actual
);

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


2

Якщо у вас є кілька тверджень в одній тестовій функції, я очікую, що вони матимуть безпосереднє відношення до тесту, який ви проводите. Наприклад,

@Test
test_Is_Date_segments_correct {

   // It is okay if you have multiple asserts checking dd, mm, yyyy, hh, mm, ss, etc. 
   // But you would not have any assert statement checking if it is string or number,
   // that is a different test and may be with multiple or single assert statement.
}

Провести багато тестів (навіть коли ти відчуваєш, що це, мабуть, надмірність) - це не погано. Ви можете стверджувати, що важливіші та найважливіші тести важливіші. Отже, коли ви стверджуєте, переконайтеся, що ваші твердження про твердження розміщені правильно, а не надто переживайте за кілька тверджень. Якщо вам потрібно більше одного, використовуйте більше одного.


1

Ціль одиничного тесту полягає в тому, щоб дати вам якомога більше інформації про те, що не вдається, а також допомогти спочатку точно визначити найбільш фундаментальні проблеми. Коли ви логічно знаєте, що одне твердження буде невдалим, враховуючи, що інше твердження провалюється, або іншими словами, існує залежність залежності між тестом, то є сенс прокрутити їх як кілька тверджень в рамках одного тесту. Це має перевагу в тому, що не засмічувати результати тесту з явними відмовами, які могли бути усунені, якби ми виступили за перше твердження в рамках одного тесту. У випадку, коли цього взаємозв'язку не існує, звичайно, перевагою було б тоді розділити ці твердження на окремі тести, оскільки в іншому випадку для виявлення цих відмов потрібні багаторазові повторення тестових циклів, щоб опрацювати всі проблеми.

Якщо ви також спроектуєте одиниці / класи таким чином, що потрібно писати надмірно складні тести, це робить менше навантаження під час тестування і, ймовірно, сприяє кращому дизайну.


1

Так, нормально мати декілька тверджень до тих пір, поки невдалий тест дає достатню інформацію, щоб можна було діагностувати збій. Це залежатиме від тестування та режимів відмов.

Правильні одиничні тести повинні бути невдалими саме з однієї причини, тому вам слід використовувати одне твердження за одиницю тесту.

Я ніколи не знаходив такі формулювання як корисні (що клас повинен мати одну причину для зміни - це приклад саме такої негідної приказки). Розглянемо твердження про те, що два рядки рівні, це семантично еквівалентно твердженню того, що довжина двох рядків однакова і кожен символ у відповідному індексі дорівнює.

Ми можемо узагальнити і сказати, що будь-яка система множинних тверджень може бути переписана як одне твердження, і будь-яке одне твердження може бути розкладене на набір менших тверджень.

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


0

Відповідь дуже проста - якщо ви протестуєте функцію, яка змінює більше одного атрибута, одного і того ж об'єкта або навіть двох різних об'єктів, а правильність функції залежить від результатів усіх цих змін, то ви хочете стверджувати що кожна з цих змін була належним чином виконана!

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

Візьміть логічну концепцію банківської транзакції - зняття суми з одного банківського рахунку у більшості випадків ДОЛЖЕН ДЛЯ ТО включати додавання цієї суми на інший рахунок. Ви НІКОЛИ не хочете відокремлювати ці дві речі, вони утворюють атомну одиницю. Можливо, ви захочете зробити дві функції (вивести / додатиMoney) і таким чином написати два різних тестових одиниці - додатково. Але ці дві дії мають відбутися в межах однієї транзакції, і ви також хочете переконатися, що транзакція працює. У цьому випадку просто недостатньо, щоб переконатися, що окремі кроки були успішними. Ви повинні перевірити обидва банківські рахунки у своєму тесті.

Можуть бути більш складні приклади, які ви б не тестували в одиничному тесті, в першу чергу, а натомість у тесті на інтеграцію чи прийняття. Але ці межі вільні, ІМХО! Вирішити це не так просто, це питання обставин і, можливо, особистих уподобань. Вилучення грошей з одного і додавання їх на інший рахунок все ще дуже проста функція і, безумовно, кандидат на тестування одиниць.


-1

Це питання пов'язане з класичною проблемою балансування між питаннями коду спагетті та лазаньї.

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

Є деякі винятки, але в цьому випадку відповідь маятника посередині - це відповідь.


-3

Я навіть не згоден із загалом "лише з однієї причини". Що важливіше, тести є короткими і читаються чітко імо.

Це не завжди досяжно, хоча і коли тест складний, (довге) описове ім'я та тестування меншої кількості речей має більше сенсу.

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