Як би вводилися помилки під час створення макетів динамічною мовою?


10

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

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

Оновлення тривіальної вибірки (на деяких складених мовах) ...

Версія 1:

Calc = {
    doMultiply(x, y) {return x * y}
}
//.... more code ....

// On some faraway remote code on a different file
Rect = {
    computeArea(l, w) {return Calc.doMultipy(x*y)}
}

// test for Rect
testComputeArea() { 
    Calc = new Mock()
    Calc.expect(doMultiply, 2, 30) // where 2 is the arity
    assertEqual(30, computeArea)
}

Тепер, у версії 2:

// I change the return types. I also update the tests for Calc
Calc = {
    doMultiply(x, y) {return {result: (x * y), success:true}}
}

... Тоді Rect викине виняток під час виконання, проте тест все одно буде успішним.


1
На сьогодні відповіді, як видається, не вистачає, що питання полягає не в тестах, що стосуються змін class X, а в тестах, class Yякі залежать від цього, Xі, таким чином, піддається тестуванню проти іншого контракту, ніж те, на що він працює у виробництві.
Барт ван Іґен Шенау

Я щойно поставив це запитання на запитання SO , що стосується введення залежностей. Див. Причина 1: Залежний клас можна змінити під час виконання (тест на думку) . У нас обох однакове мислення, але нам не вистачає великих пояснень.
Скотт Коутс

Я перечитую ваше запитання, але я трохи заплутався у тлумаченні. Чи можете ви навести приклад?
Скотт Коутс

Відповіді:


2

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

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

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


2

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

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

Для того , щоб не пропустити затвердження, ви можете використовувати такі інструменти, як аналіз покриття коди (він не скаже вам , які частини вашого коду тестує, але це буде вам сказати , що деталі виразно НЕ тестувалися), цикломатическая складність і складність NPath (які дають вам нижню межу на ряд тверджень , необхідних для досягнення повного C1 і C2 покриття коду) і мутації тестувальників (які INJECT мутації в вашому коді , такі як поворот trueдо false, негативні числа в позитивну, об'єкти в і nullт.д. , і перевірити , що робить тести невданими).


+1 Або щось не так з тестами чи кодом насправді не змінилося. Це зайняло мені час, щоб звикнути після багатьох років C ++ / Java. Контракт між частинами в динамічних мовах коду не повинен бути ЩО повертається, але те, що повертається, містить те, що він може робити.
MrFox

@suslik: Насправді мова йде не про статичні та динамічні мови, а про абстрагування даних за допомогою абстрактних типів даних проти об'єктно-орієнтованих абстракцій. Здатність одного об'єкта імітувати інший об'єкт до тих пір, поки він поводиться невідрізно (навіть якщо вони мають абсолютно різні типи та екземпляри абсолютно різних класів) є основоположним для всієї ідеї ОО. Або по-іншому: якщо два об'єкти поводяться однаково, вони одного типу, незалежно від того, що говорять їхні класи. Іншим способом: класи не типи. На жаль, Java і C # помиляються.
Йорг W Міттаг

Якщо припустити, що макетований блок на 100% покритий і немає жодного твердження: як щодо більш тонкої зміни на зразок "повинен повернути нуль для foo" на "повинен повернути порожній рядок для foo". Якщо хтось змінить цю одиницю та її тест, деякі споживаючі одиниці можуть зламатися, і через макет це прозоро. (І ні: під час написання або використання макетного модуля, відповідно до "контракту", не потрібно обробляти порожні рядки як зворотні значення, оскільки він повинен повертати не порожні рядки або нулі;)). (Тільки належне тестування інтеграції обох модулів у взаємодії могло це наздогнати.)
try-catch-нарешті
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.